mirror of
https://github.com/penpot/penpot.git
synced 2026-01-27 15:51:32 -05:00
Compare commits
214 Commits
alotor-was
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fda31624c1 | ||
|
|
7f640569bd | ||
|
|
91f1323802 | ||
|
|
dbd4a2366f | ||
|
|
cbb6d098a7 | ||
|
|
b6f5000d1c | ||
|
|
0527124f2f | ||
|
|
faf91ac70d | ||
|
|
9ca76c745f | ||
|
|
e8fd4698c9 | ||
|
|
0ab126748f | ||
|
|
71a5ab9913 | ||
|
|
61969f3eb5 | ||
|
|
bd2ef8057e | ||
|
|
9808b6ca57 | ||
|
|
2523096fdd | ||
|
|
de41cb5488 | ||
|
|
8e63c4e3e8 | ||
|
|
b40ccaf030 | ||
|
|
7d3ac38749 | ||
|
|
d5abc52dac | ||
|
|
3112b240a0 | ||
|
|
56fd66b91a | ||
|
|
3b96eb5476 | ||
|
|
7a842ce36a | ||
|
|
ea25c5db99 | ||
|
|
ce1796eb02 | ||
|
|
6f0685ba8e | ||
|
|
1ce0b60e3d | ||
|
|
d433fd25c1 | ||
|
|
bb0e9b47cb | ||
|
|
ef80901400 | ||
|
|
c5f03d711a | ||
|
|
5306bed548 | ||
|
|
92a319ddd1 | ||
|
|
68a6d4c9a8 | ||
|
|
72cc5ee349 | ||
|
|
804695b48b | ||
|
|
20c8fbf314 | ||
|
|
e02536f8d4 | ||
|
|
d9c56da705 | ||
|
|
75248aec4e | ||
|
|
f0d9429775 | ||
|
|
62ecf48bdb | ||
|
|
18de7f1db6 | ||
|
|
2b2941bd25 | ||
|
|
f2d561eff7 | ||
|
|
418b65a287 | ||
|
|
d4e7810eba | ||
|
|
1d1d32ad39 | ||
|
|
fb08dc65c8 | ||
|
|
927ac93fa7 | ||
|
|
e546a7c614 | ||
|
|
058c20c2e2 | ||
|
|
5016b2a7bf | ||
|
|
43ae213659 | ||
|
|
dc973dac36 | ||
|
|
4467827218 | ||
|
|
6470db8d5f | ||
|
|
dc44156b53 | ||
|
|
f0e53d70ae | ||
|
|
ef73a263b2 | ||
|
|
9b1e007a49 | ||
|
|
ea8632e56a | ||
|
|
2d00e64ede | ||
|
|
1246250198 | ||
|
|
34f2943dcd | ||
|
|
3fb78116b8 | ||
|
|
072e415b9e | ||
|
|
67a904824c | ||
|
|
68184209be | ||
|
|
d2295862b4 | ||
|
|
23cbf33d1b | ||
|
|
b4ff0ccf3a | ||
|
|
6c6666a39a | ||
|
|
c1335961b4 | ||
|
|
eaf64b6e16 | ||
|
|
560a0d09d5 | ||
|
|
b70eb768e0 | ||
|
|
4397ede5c1 | ||
|
|
e0910db99e | ||
|
|
079b3fbfad | ||
|
|
299f628951 | ||
|
|
32d0fe6463 | ||
|
|
cecd3d4a90 | ||
|
|
1c2c0987f5 | ||
|
|
0418147e74 | ||
|
|
9e0ba4429a | ||
|
|
7499a5bca6 | ||
|
|
6cd5bc76d7 | ||
|
|
bbe6ee2e19 | ||
|
|
fb6d8309b6 | ||
|
|
689467bcf9 | ||
|
|
7724450037 | ||
|
|
368fa954ce | ||
|
|
0ecb2bc838 | ||
|
|
e92f3fb3cb | ||
|
|
5193cfd56e | ||
|
|
813d5d8e69 | ||
|
|
84f1ff092d | ||
|
|
f2b082b93e | ||
|
|
1f41bef4a9 | ||
|
|
fdf5bb250b | ||
|
|
786736fadd | ||
|
|
1ff6e00398 | ||
|
|
25455523ad | ||
|
|
8dfeb21978 | ||
|
|
ad2833bb7a | ||
|
|
21911e898f | ||
|
|
538073debf | ||
|
|
214b0efa02 | ||
|
|
661436ecae | ||
|
|
0d5fe6e527 | ||
|
|
e7230d9da4 | ||
|
|
5054f6bc38 | ||
|
|
38396ba299 | ||
|
|
b1997a83b3 | ||
|
|
68f5671eab | ||
|
|
92976143bb | ||
|
|
dd2d03e6a0 | ||
|
|
fd675e0194 | ||
|
|
c98373658e | ||
|
|
2e400768b7 | ||
|
|
01e42b0458 | ||
|
|
7529673812 | ||
|
|
d2dad35d7a | ||
|
|
f7b5266304 | ||
|
|
09c23256b7 | ||
|
|
1ae1c0460e | ||
|
|
291c7349db | ||
|
|
b605a3b53d | ||
|
|
cc2dab2756 | ||
|
|
d0c0664338 | ||
|
|
2240f25143 | ||
|
|
93a5ec2f5d | ||
|
|
d6784771a8 | ||
|
|
930c814ded | ||
|
|
1a5a69bca2 | ||
|
|
9ad323a220 | ||
|
|
bcaf76d055 | ||
|
|
5fa4368d70 | ||
|
|
b8efd2518d | ||
|
|
7b2271ec38 | ||
|
|
2240d93069 | ||
|
|
3f4506284b | ||
|
|
af1dfd91aa | ||
|
|
24feebd73b | ||
|
|
33e5a9a538 | ||
|
|
9c69b07a62 | ||
|
|
56f5be4f37 | ||
|
|
8a70204d41 | ||
|
|
57a27f7e7f | ||
|
|
3b0b2a78d6 | ||
|
|
10bf4610df | ||
|
|
77e8414aea | ||
|
|
20ecf3b066 | ||
|
|
49b1032973 | ||
|
|
5ba7dd8c56 | ||
|
|
38b5125186 | ||
|
|
6677ae83d4 | ||
|
|
0737c055f0 | ||
|
|
4b88748fe3 | ||
|
|
92107e5b1e | ||
|
|
ebc0e3a23c | ||
|
|
ebe4f2da50 | ||
|
|
a07c1d6eaa | ||
|
|
613bfda955 | ||
|
|
f7ef6618e5 | ||
|
|
fe334d9cbe | ||
|
|
268b883c73 | ||
|
|
f6a4effa29 | ||
|
|
ced848077e | ||
|
|
7d9d318539 | ||
|
|
9781fceadb | ||
|
|
3178bd9a27 | ||
|
|
e5d677f449 | ||
|
|
6bf928893c | ||
|
|
53dd90aa24 | ||
|
|
9fd0f6a8f3 | ||
|
|
638c3356d3 | ||
|
|
6879f54e5d | ||
|
|
a71baa5a78 | ||
|
|
8e4a89bd1c | ||
|
|
7aad9da285 | ||
|
|
ab57a4ae52 | ||
|
|
266ee29bb9 | ||
|
|
69ca86bb6c | ||
|
|
ee14a845fc | ||
|
|
73639f5d16 | ||
|
|
9bd106b2bc | ||
|
|
59c75afc7b | ||
|
|
bbc81586e3 | ||
|
|
c9c30eab75 | ||
|
|
86ba9280db | ||
|
|
5800cc4bb2 | ||
|
|
aa29a34c4c | ||
|
|
3276129cc7 | ||
|
|
67a96de475 | ||
|
|
48785b4846 | ||
|
|
3f0573f95d | ||
|
|
d94a2a8881 | ||
|
|
1c237a0968 | ||
|
|
b7eaeffa88 | ||
|
|
722fcc1f82 | ||
|
|
2ad42cfd9b | ||
|
|
795f65632a | ||
|
|
7819e6c440 | ||
|
|
952f622ce9 | ||
|
|
a6c6f97f47 | ||
|
|
88424eb54a | ||
|
|
de9a21121a | ||
|
|
cea10308b7 | ||
|
|
5223c9c881 | ||
|
|
be62fa10c4 |
@@ -45,6 +45,15 @@
|
||||
:potok/reify-type
|
||||
{:level :error}
|
||||
|
||||
:redundant-primitive-coercion
|
||||
{:level :off}
|
||||
|
||||
:unused-excluded-var
|
||||
{:level :off}
|
||||
|
||||
:unresolved-excluded-var
|
||||
{:level :off}
|
||||
|
||||
:missing-protocol-method
|
||||
{:level :off}
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
:remove-multiple-non-indenting-spaces? false
|
||||
:remove-surrounding-whitespace? true
|
||||
:remove-consecutive-blank-lines? false
|
||||
:indent-line-comments? true
|
||||
:parallel? true
|
||||
:align-form-columns? false
|
||||
;; :align-map-columns? false
|
||||
;; :align-single-column-lines? false
|
||||
:extra-indents {rumext.v2/fnc [[:inner 0]]
|
||||
cljs.test/async [[:inner 0]]
|
||||
promesa.exec/thread [[:inner 0]]
|
||||
|
||||
38
.github/ISSUE_TEMPLATE/new-render-bug-report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/new-render-bug-report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: New Render Bug Report
|
||||
about: Create a report about the bugs you have found in the new render
|
||||
title: ''
|
||||
labels: new render
|
||||
assignees: claragvinola
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Steps to Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots or screen recordings**
|
||||
If applicable, add screenshots or screen recording to help illustrate your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
2
.github/workflows/build-bundle.yml
vendored
2
.github/workflows/build-bundle.yml
vendored
@@ -40,7 +40,7 @@ on:
|
||||
jobs:
|
||||
build-bundle:
|
||||
name: Build and Upload Penpot Bundle
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: penpot-runner-01
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
7
.github/workflows/build-docker-devenv.yml
vendored
7
.github/workflows/build-docker-devenv.yml
vendored
@@ -7,9 +7,14 @@ jobs:
|
||||
build-and-push:
|
||||
name: Build and push DevEnv Docker image
|
||||
environment: release-admins
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: penpot-runner-02
|
||||
|
||||
steps:
|
||||
- name: Set common environment variables
|
||||
run: |
|
||||
# Each job execution will use its own docker configuration.
|
||||
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
|
||||
16
.github/workflows/build-docker.yml
vendored
16
.github/workflows/build-docker.yml
vendored
@@ -19,9 +19,14 @@ on:
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build and Push Penpot Docker Images
|
||||
runs-on: ubuntu-24.04-arm
|
||||
runs-on: penpot-runner-02
|
||||
|
||||
steps:
|
||||
- name: Set common environment variables
|
||||
run: |
|
||||
# Each job execution will use its own docker configuration.
|
||||
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -66,6 +71,15 @@ jobs:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
# To avoid the “429 Too Many Requests” error when downloading
|
||||
# images from DockerHub for unregistered users.
|
||||
# https://docs.docker.com/docker-hub/usage/
|
||||
- name: Login to DockerHub Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.PUB_DOCKER_USERNAME }}
|
||||
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata (tags, labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
|
||||
21
.github/workflows/build-nitrate-module.yml
vendored
21
.github/workflows/build-nitrate-module.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: _NITRATE MODULE
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '36 5-20 * * 1-5'
|
||||
|
||||
jobs:
|
||||
build-bundle:
|
||||
uses: ./.github/workflows/build-bundle.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "nitrate-module"
|
||||
build_wasm: "yes"
|
||||
build_storybook: "yes"
|
||||
|
||||
build-docker:
|
||||
needs: build-bundle
|
||||
uses: ./.github/workflows/build-docker.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "nitrate-module"
|
||||
2
.github/workflows/commit-checker.yml
vendored
2
.github/workflows/commit-checker.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Check Commit Type
|
||||
uses: gsactions/commit-message-checker@v2
|
||||
with:
|
||||
pattern: '^(((:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle|rewind|construction_worker):)\s[A-Z].*[^.])|(Merge|Revert).+[^.])$'
|
||||
pattern: '^(((:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle|rewind|construction_worker):)\s[A-Z].*[^.])|(Merge|Revert|Reapply).+[^.])$'
|
||||
flags: 'gm'
|
||||
error: 'Commit should match CONTRIBUTING.md guideline'
|
||||
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request
|
||||
|
||||
42
.github/workflows/plugins-deploy-api-doc.yml
vendored
42
.github/workflows/plugins-deploy-api-doc.yml
vendored
@@ -7,11 +7,11 @@ on:
|
||||
- staging
|
||||
- main
|
||||
paths:
|
||||
- "plugins/libs/plugin-types/index.d.ts"
|
||||
- "plugins/libs/plugin-types/REAME.md"
|
||||
- "plugins/tools/typedoc.css"
|
||||
- "plugins/CHANGELOG.md"
|
||||
- "plugins/wrangle-penpot-plugins-api-doc.toml"
|
||||
- 'plugins/libs/plugin-types/index.d.ts'
|
||||
- 'plugins/libs/plugin-types/REAME.md'
|
||||
- 'plugins/tools/typedoc.css'
|
||||
- 'plugins/CHANGELOG.md'
|
||||
- 'plugins/wrangler-penpot-plugins-api-doc.toml'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gh_ref:
|
||||
@@ -86,16 +86,40 @@ jobs:
|
||||
run: |
|
||||
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||
case "$REF" in
|
||||
main) echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV ;;
|
||||
staging) echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV ;;
|
||||
develop) echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV ;;
|
||||
main)
|
||||
echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||
staging)
|
||||
echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||
develop)
|
||||
echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||
esac
|
||||
|
||||
- name: Set the custom url
|
||||
working-directory: plugins
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-api-doc.toml
|
||||
|
||||
- name: Deploy to Cloudflare Workers
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
workingDirectory: plugins
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --config wrangle-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }}
|
||||
command: deploy --config wrangler-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }}
|
||||
|
||||
- name: Notify Mattermost
|
||||
if: failure()
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||
TEXT: |
|
||||
❌ 🧩📚 *[PENPOT PLUGINS] Error deploying API documentation.*
|
||||
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
@infra
|
||||
|
||||
127
.github/workflows/plugins-deploy-package.yml
vendored
Normal file
127
.github/workflows/plugins-deploy-package.yml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
name: Plugins/package deployer
|
||||
|
||||
on:
|
||||
# Deploy package from manual action
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gh_ref:
|
||||
description: 'Name of the branch'
|
||||
type: choice
|
||||
required: true
|
||||
default: 'develop'
|
||||
options:
|
||||
- develop
|
||||
- staging
|
||||
- main
|
||||
plugin_name:
|
||||
description: 'Pluging name (like plugins/apps/<plugin_name>-plugin)'
|
||||
type: string
|
||||
required: true
|
||||
workflow_call:
|
||||
inputs:
|
||||
gh_ref:
|
||||
description: 'Name of the branch'
|
||||
type: string
|
||||
required: true
|
||||
default: 'develop'
|
||||
plugin_name:
|
||||
description: 'Publig name (from plugins/apps/<plugin_name>-plugin)'
|
||||
type: string
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: penpot-runner-01
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.gh_ref }}
|
||||
|
||||
# START: Setup Node and PNPM enabling cache
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
- name: Enable PNPM
|
||||
working-directory: ./plugins
|
||||
shell: bash
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
|
||||
- name: Get pnpm store path
|
||||
id: pnpm-store
|
||||
working-directory: ./plugins
|
||||
shell: bash
|
||||
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache pnpm store
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-
|
||||
# END: Setup Node and PNPM enabling cache
|
||||
|
||||
- name: Install deps
|
||||
working-directory: ./plugins
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm install --no-frozen-lockfile;
|
||||
pnpm add -D -w wrangler@latest;
|
||||
|
||||
- name: "Build package for ${{ inputs.plugin_name }}-plugin"
|
||||
working-directory: plugins
|
||||
shell: bash
|
||||
run: npx nx build ${{ inputs.plugin_name }}-plugin
|
||||
|
||||
- name: Select Worker name
|
||||
run: |
|
||||
REF="${{ inputs.gh_ref }}"
|
||||
case "$REF" in
|
||||
main)
|
||||
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pro" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||
staging)
|
||||
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pre" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||
develop)
|
||||
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-hourly" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||
esac
|
||||
|
||||
- name: Set the custom url
|
||||
working-directory: plugins
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" apps/${{ inputs.plugin_name }}-plugin/wrangler.toml
|
||||
|
||||
- name: Deploy to Cloudflare Workers
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
workingDirectory: plugins
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --config apps/${{ inputs.plugin_name }}-plugin/wrangler.toml --name ${{ env.WORKER_NAME }}
|
||||
|
||||
- name: Notify Mattermost
|
||||
if: failure()
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||
TEXT: |
|
||||
❌ 🧩📦 *[PENPOT PLUGINS] Error deploying ${{ env.WORKER_NAME }}.*
|
||||
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||
Plugin name: `${{ inputs.plugin_name }}-plugin`
|
||||
Cloudflare worker name: `${{ env.WORKER_NAME }}`
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
@infra
|
||||
143
.github/workflows/plugins-deploy-packages.yml
vendored
Normal file
143
.github/workflows/plugins-deploy-packages.yml
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
name: Plugins/packages deployer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- staging
|
||||
- main
|
||||
paths:
|
||||
- 'plugins/apps/*-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gh_ref:
|
||||
description: 'Name of the branch'
|
||||
type: choice
|
||||
required: true
|
||||
default: 'develop'
|
||||
options:
|
||||
- develop
|
||||
- staging
|
||||
- main
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
colors_to_tokens: ${{ steps.filter.outputs.colors_to_tokens }}
|
||||
create_palette: ${{ steps.filter.outputs.create_palette }}
|
||||
lorem_ipsum: ${{ steps.filter.outputs.lorem_ipsum }}
|
||||
rename_layers: ${{ steps.filter.outputs.rename_layers }}
|
||||
contrast: ${{ steps.filter.outputs.contrast }}
|
||||
icons: ${{ steps.filter.outputs.icons }}
|
||||
poc_state: ${{ steps.filter.outputs.poc_state }}
|
||||
table: ${{ steps.filter.outputs.table }}
|
||||
# [For new plugins]
|
||||
# Add more outputs here
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: filter
|
||||
uses: dorny/paths-filter@v3
|
||||
with:
|
||||
filters: |
|
||||
colors_to_tokens:
|
||||
- 'plugins/apps/colors-to-tokens-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
contrast:
|
||||
- 'plugins/apps/contrast-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
create_palette:
|
||||
- 'plugins/apps/create-palette-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
icons:
|
||||
- 'plugins/apps/icons-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
lorem_ipsum:
|
||||
- 'plugins/apps/lorem-ipsum-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
rename_layers:
|
||||
- 'plugins/apps/rename-layers-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
table:
|
||||
- 'plugins/apps/table-plugin/**'
|
||||
- 'libs/plugins-styles/**'
|
||||
# [For new plugins]
|
||||
# Add more plugin filters here
|
||||
# another_plugin:
|
||||
# - 'plugins/apps/another-plugin/**'
|
||||
# - 'libs/plugins-styles/**'
|
||||
|
||||
colors-to-tokens-plugin:
|
||||
needs: detect-changes
|
||||
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.colors_to_tokens == 'true'
|
||||
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
plugin_name: colors-to-tokens
|
||||
|
||||
contrast-plugin:
|
||||
needs: detect-changes
|
||||
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.contrast == 'true'
|
||||
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
plugin_name: contrast
|
||||
|
||||
create-palette-plugin:
|
||||
needs: detect-changes
|
||||
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.create_palette == 'true'
|
||||
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
plugin_name: create-palette
|
||||
|
||||
icons-plugin:
|
||||
needs: detect-changes
|
||||
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.icons == 'true'
|
||||
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
plugin_name: icons
|
||||
|
||||
lorem-ipsum-plugin:
|
||||
needs: detect-changes
|
||||
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.lorem_ipsum == 'true'
|
||||
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
plugin_name: lorem-ipsum
|
||||
|
||||
rename-layers-plugin:
|
||||
needs: detect-changes
|
||||
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.rename_layers == 'true'
|
||||
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
plugin_name: rename-layers
|
||||
|
||||
table-plugin:
|
||||
needs: detect-changes
|
||||
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.table == 'true'
|
||||
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
plugin_name: table
|
||||
|
||||
# [For new plugins]
|
||||
# Add more jobs for other plugins below, following the same pattern
|
||||
# another-plugin:
|
||||
# needs: detect-changes
|
||||
# if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.another_plugin == 'true'
|
||||
# uses: ./.github/workflows/plugins-deploy-package.yml
|
||||
# secrets: inherit
|
||||
# with:
|
||||
# gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||
# plugin_name: another
|
||||
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
name: Plugins/styles-doc deployer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- staging
|
||||
- main
|
||||
paths:
|
||||
- 'plugins/apps/example-styles/**'
|
||||
- 'plugins/libs/plugins-styles/**'
|
||||
- 'plugins/wrangler-penpot-plugins-styles-doc.toml'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gh_ref:
|
||||
description: 'Name of the branch'
|
||||
type: choice
|
||||
required: true
|
||||
default: 'develop'
|
||||
options:
|
||||
- develop
|
||||
- staging
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Extract some useful variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ steps.vars.outputs.gh_ref }}
|
||||
|
||||
# START: Setup Node and PNPM enabling cache
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
- name: Enable PNPM
|
||||
working-directory: ./plugins
|
||||
shell: bash
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
|
||||
- name: Get pnpm store path
|
||||
id: pnpm-store
|
||||
working-directory: ./plugins
|
||||
shell: bash
|
||||
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache pnpm store
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-
|
||||
# END: Setup Node and PNPM enabling cache
|
||||
|
||||
- name: Install deps
|
||||
working-directory: ./plugins
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm install --no-frozen-lockfile;
|
||||
pnpm add -D -w wrangler@latest;
|
||||
|
||||
- name: Build styles
|
||||
working-directory: plugins
|
||||
shell: bash
|
||||
run: npx nx run example-styles:build
|
||||
|
||||
- name: Select Worker name
|
||||
run: |
|
||||
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||
case "$REF" in
|
||||
main)
|
||||
echo "WORKER_NAME=penpot-plugins-styles-doc-pro" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=styles-doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||
staging)
|
||||
echo "WORKER_NAME=penpot-plugins-styles-doc-pre" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=styles-doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||
develop)
|
||||
echo "WORKER_NAME=penpot-plugins-styles-doc-hourly" >> $GITHUB_ENV
|
||||
echo "WORKER_URI=styles-doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||
esac
|
||||
|
||||
- name: Set the custom url
|
||||
working-directory: plugins
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-styles-doc.toml
|
||||
|
||||
- name: Deploy to Cloudflare Workers
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
workingDirectory: plugins
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: deploy --config wrangler-penpot-plugins-styles-doc.toml --name ${{ env.WORKER_NAME }}
|
||||
|
||||
- name: Notify Mattermost
|
||||
if: failure()
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||
TEXT: |
|
||||
❌ 🧩💅 *[PENPOT PLUGINS] Error deploying Styles documentation.*
|
||||
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
@infra
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -21,6 +21,7 @@
|
||||
.rebel_readline_history
|
||||
.repl
|
||||
.shadow-cljs
|
||||
.pnpm-store/
|
||||
/*.jpg
|
||||
/*.md
|
||||
/*.png
|
||||
@@ -44,6 +45,7 @@
|
||||
/backend/resources/public/media
|
||||
/backend/target/
|
||||
/backend/experiments
|
||||
/backend/scripts/_env.local
|
||||
/bundle*
|
||||
/cd.md
|
||||
/clj-profiler/
|
||||
@@ -54,6 +56,8 @@
|
||||
/exporter/target
|
||||
/frontend/.storybook/preview-body.html
|
||||
/frontend/.storybook/preview-head.html
|
||||
/frontend/playwright-report/
|
||||
/frontend/text-editor/src/wasm/
|
||||
/frontend/dist/
|
||||
/frontend/npm-debug.log
|
||||
/frontend/out/
|
||||
@@ -72,6 +76,7 @@
|
||||
/library/target/
|
||||
/library/*.zip
|
||||
/external
|
||||
/penpot-nitrate
|
||||
|
||||
clj-profiler/
|
||||
node_modules
|
||||
|
||||
105
.gitpod.yml
105
.gitpod.yml
@@ -1,105 +0,0 @@
|
||||
image:
|
||||
file: docker/gitpod/Dockerfile
|
||||
|
||||
ports:
|
||||
# nginx
|
||||
- port: 3449
|
||||
onOpen: open-preview
|
||||
|
||||
# frontend nREPL
|
||||
- port: 3447
|
||||
onOpen: ignore
|
||||
visibility: private
|
||||
|
||||
# frontend shadow server
|
||||
- port: 3448
|
||||
onOpen: ignore
|
||||
visibility: private
|
||||
|
||||
# backend
|
||||
- port: 6060
|
||||
onOpen: ignore
|
||||
|
||||
# exporter shadow server
|
||||
- port: 9630
|
||||
onOpen: ignore
|
||||
visibility: private
|
||||
|
||||
# exporter http server
|
||||
- port: 6061
|
||||
onOpen: ignore
|
||||
|
||||
# mailhog web interface
|
||||
- port: 8025
|
||||
onOpen: ignore
|
||||
|
||||
# mailhog postfix
|
||||
- port: 1025
|
||||
onOpen: ignore
|
||||
|
||||
# postgres
|
||||
- port: 5432
|
||||
onOpen: ignore
|
||||
|
||||
# redis
|
||||
- port: 6379
|
||||
onOpen: ignore
|
||||
|
||||
# openldap
|
||||
- port: 389
|
||||
onOpen: ignore
|
||||
|
||||
tasks:
|
||||
# https://github.com/gitpod-io/gitpod/issues/666#issuecomment-534347856
|
||||
- name: gulp
|
||||
command: >
|
||||
cd $GITPOD_REPO_ROOT/frontend/;
|
||||
yarn && gp sync-done 'frontend-yarn';
|
||||
npx gulp --theme=${PENPOT_THEME} watch
|
||||
|
||||
- name: frontend shadow watch
|
||||
command: >
|
||||
cd $GITPOD_REPO_ROOT/frontend/;
|
||||
gp sync-await 'frontend-yarn';
|
||||
npx shadow-cljs watch main
|
||||
|
||||
- init: gp await-port 5432 && psql -f $GITPOD_REPO_ROOT/docker/gitpod/files/postgresql_init.sql
|
||||
name: backend
|
||||
command: >
|
||||
cd $GITPOD_REPO_ROOT/backend/;
|
||||
./scripts/start-dev
|
||||
|
||||
- name: exporter shadow watch
|
||||
command:
|
||||
cd $GITPOD_REPO_ROOT/exporter/;
|
||||
gp sync-await 'frontend-yarn';
|
||||
yarn && npx shadow-cljs watch main
|
||||
|
||||
- name: exporter web server
|
||||
command: >
|
||||
cd $GITPOD_REPO_ROOT/exporter/;
|
||||
./scripts/wait-and-start.sh
|
||||
|
||||
- name: signed terminal
|
||||
before: >
|
||||
[[ ! -z ${GNUGPG} ]] &&
|
||||
cd ~ &&
|
||||
rm -rf .gnupg &&
|
||||
echo ${GNUGPG} | base64 -d | tar --no-same-owner -xzvf -
|
||||
init: >
|
||||
[[ ! -z ${GNUGPG_KEY} ]] &&
|
||||
git config --global commit.gpgsign true &&
|
||||
git config --global user.signingkey ${GNUGPG_KEY}
|
||||
command: cd $GITPOD_REPO_ROOT
|
||||
|
||||
- name: redis
|
||||
command: redis-server
|
||||
|
||||
- before: go get github.com/mailhog/MailHog
|
||||
name: mailhog
|
||||
command: MailHog
|
||||
|
||||
- name: Nginx
|
||||
command: >
|
||||
nginx &&
|
||||
multitail /var/log/nginx/access.log -I /var/log/nginx/error.log
|
||||
11
.yarnrc.yml
11
.yarnrc.yml
@@ -1,11 +0,0 @@
|
||||
enableGlobalCache: true
|
||||
|
||||
enableImmutableCache: false
|
||||
|
||||
enableImmutableInstalls: false
|
||||
|
||||
enableTelemetry: false
|
||||
|
||||
httpTimeout: 600000
|
||||
|
||||
nodeLinker: node-modules
|
||||
40
CHANGES.md
40
CHANGES.md
@@ -1,5 +1,37 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.14.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features & Enhancements
|
||||
|
||||
- Remap references when renaming tokens [Taiga #10202](https://tree.taiga.io/project/penpot/us/10202)
|
||||
- Tokens panel nested path view [Taiga #9966](https://tree.taiga.io/project/penpot/us/9966)
|
||||
- Improve usability of lock and hide buttons in the layer panel. [Taiga #12916](https://tree.taiga.io/project/penpot/issue/12916)
|
||||
- Optimize sidebar performance for deeply nested shapes [Taiga #13017](https://tree.taiga.io/project/penpot/task/13017)
|
||||
- Remove tokens path node and bulk remove tokens [Taiga #13007](https://tree.taiga.io/project/penpot/us/13007)
|
||||
- Replace themes management modal radio buttons for switches [Taiga #9215](https://tree.taiga.io/project/penpot/us/9215)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Remove whitespaces from asset export filename [Github #8133](https://github.com/penpot/penpot/pull/8133)
|
||||
- Fix prototype connections lost when switching between variants [Taiga #12812](https://tree.taiga.io/project/penpot/issue/12812)
|
||||
- Fix wrong image in the onboarding invitation block [Taiga #13040](https://tree.taiga.io/project/penpot/issue/13040)
|
||||
- Fix wrong register image [Taiga #12955](https://tree.taiga.io/project/penpot/task/12955)
|
||||
- Fix error message on components doesn't close automatically [Taiga #12012](https://tree.taiga.io/project/penpot/issue/12012)
|
||||
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
||||
- Fix incorrect default option on tokens import dialog [Github #8051](https://github.com/penpot/penpot/pull/8051)
|
||||
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
|
||||
- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058)
|
||||
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||
- Fix boolean operators in menu for boards [Taiga #13174](https://tree.taiga.io/project/penpot/issue/13174)
|
||||
|
||||
## 2.13.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
@@ -23,6 +55,7 @@
|
||||
- Fix wrong board size presets in Android [Taiga #12339](https://tree.taiga.io/project/penpot/issue/12339)
|
||||
- Fix problem with grid layout components and auto sizing [Github #7797](https://github.com/penpot/penpot/issues/7797)
|
||||
- Fix some alignments on inspect tab [Taiga #12915](https://tree.taiga.io/project/penpot/issue/12915)
|
||||
- Fix problem with text editor maintaining previous styles [Taiga #12835](https://tree.taiga.io/project/penpot/issue/12835)
|
||||
- Fix color assets from shared libraries not appearing as assets in Selected colors panel [Taiga #12957](https://tree.taiga.io/project/penpot/issue/12957)
|
||||
- Fix CSS generated box-shadow property [Taiga #12997](https://tree.taiga.io/project/penpot/issue/12997)
|
||||
- Fix inner shadow selector on shadow token [Taiga #12951](https://tree.taiga.io/project/penpot/issue/12951)
|
||||
@@ -37,7 +70,8 @@
|
||||
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
||||
|
||||
- Fix several race conditions on path editor [Github #8187](https://github.com/penpot/penpot/pull/8187)
|
||||
- Fix app freeze when introducing an error on a very long token name [Taiga #13214](https://tree.taiga.io/project/penpot/issue/13214)
|
||||
|
||||
## 2.12.1
|
||||
|
||||
@@ -47,7 +81,6 @@
|
||||
- Fix problem with style in fonts input [Taiga #12935](https://tree.taiga.io/project/penpot/issue/12935)
|
||||
- Fix problem with path editor and right click [Github #7917](https://github.com/penpot/penpot/issues/7917)
|
||||
|
||||
|
||||
## 2.12.0
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
@@ -59,7 +92,6 @@ The backend RPC API URLS are changed from `/api/rpc/command/<name>` to
|
||||
compatibility; however, if you are a user of this API, it is strongly
|
||||
recommended that you adapt your code to use the new PATH.
|
||||
|
||||
|
||||
#### Updated SSO Callback URL
|
||||
|
||||
The OAuth / Single Sign-On (SSO) callback endpoint has changed to
|
||||
@@ -92,7 +124,6 @@ This update standardizes all authentication flows under the single URL
|
||||
and makis it more modular, enabling the ability to configure SSO auth
|
||||
provider dinamically.
|
||||
|
||||
|
||||
#### Changes on default docker compose
|
||||
|
||||
We have updated the `docker/images/docker-compose.yaml` with a small
|
||||
@@ -156,7 +187,6 @@ example. It's still usable as before, we just removed the example.
|
||||
|
||||
- Deprecated configuration variables with the prefix `PENPOT_ASSETS_*`, and will be
|
||||
removed in future versions:
|
||||
|
||||
- The `PENPOT_ASSETS_STORAGE_BACKEND` becomes `PENPOT_OBJECTS_STORAGE_BACKEND` and its
|
||||
values passes from (`assets-fs` or `assets-s3`) to (`fs` or `s3`)
|
||||
- The `PENPOT_STORAGE_ASSETS_FS_DIRECTORY` becomes `PENPOT_OBJECTS_STORAGE_FS_DIRECTORY`
|
||||
|
||||
@@ -120,17 +120,12 @@ them on your system, you can run them with:
|
||||
|
||||
```bash
|
||||
# Check formatting
|
||||
yarn fmt:clj:check
|
||||
./scripts/fmt
|
||||
|
||||
# Check and fix formatting
|
||||
yarn fmt:clj
|
||||
|
||||
# Run the linter
|
||||
yarn lint:clj
|
||||
# Lint
|
||||
./scripts/lint
|
||||
```
|
||||
|
||||
There are more choices in `package.json`.
|
||||
|
||||
Ideally, you should run these commands as git pre-commit hooks. A convenient way
|
||||
of defining them is to use [Husky](https://typicode.github.io/husky/#/).
|
||||
|
||||
|
||||
7
backend/.gitignore
vendored
7
backend/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
|
||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/penpot/penpot"
|
||||
|
||||
306
backend/pnpm-lock.yaml
generated
Normal file
306
backend/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,306 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
luxon:
|
||||
specifier: ^3.4.4
|
||||
version: 3.7.2
|
||||
sax:
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.3
|
||||
devDependencies:
|
||||
nodemon:
|
||||
specifier: ^3.1.2
|
||||
version: 3.1.11
|
||||
source-map-support:
|
||||
specifier: ^0.5.21
|
||||
version: 0.5.21
|
||||
ws:
|
||||
specifier: ^8.17.0
|
||||
version: 8.18.3
|
||||
|
||||
packages:
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||
|
||||
braces@3.0.3:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
fill-range@7.1.1:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
has-flag@3.0.0:
|
||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
ignore-by-default@1.0.1:
|
||||
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
luxon@3.7.2:
|
||||
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
nodemon@3.1.11:
|
||||
resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
pstree.remy@1.1.8:
|
||||
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
||||
sax@1.4.3:
|
||||
resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==}
|
||||
|
||||
semver@7.7.3:
|
||||
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||
|
||||
source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
supports-color@5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
touch@3.1.1:
|
||||
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
||||
hasBin: true
|
||||
|
||||
undefsafe@2.0.5:
|
||||
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
|
||||
|
||||
ws@8.18.3:
|
||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
concat-map: 0.0.1
|
||||
|
||||
braces@3.0.3:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
chokidar@3.6.0:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.3
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
debug@4.4.3(supports-color@5.5.0):
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
optionalDependencies:
|
||||
supports-color: 5.5.0
|
||||
|
||||
fill-range@7.1.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
|
||||
has-flag@3.0.0: {}
|
||||
|
||||
ignore-by-default@1.0.1: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
|
||||
is-extglob@2.1.1: {}
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
luxon@3.7.2: {}
|
||||
|
||||
minimatch@3.1.2:
|
||||
dependencies:
|
||||
brace-expansion: 1.1.12
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nodemon@3.1.11:
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
debug: 4.4.3(supports-color@5.5.0)
|
||||
ignore-by-default: 1.0.1
|
||||
minimatch: 3.1.2
|
||||
pstree.remy: 1.1.8
|
||||
semver: 7.7.3
|
||||
simple-update-notifier: 2.0.0
|
||||
supports-color: 5.5.0
|
||||
touch: 3.1.1
|
||||
undefsafe: 2.0.5
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
pstree.remy@1.1.8: {}
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
||||
sax@1.4.3: {}
|
||||
|
||||
semver@7.7.3: {}
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
dependencies:
|
||||
semver: 7.7.3
|
||||
|
||||
source-map-support@0.5.21:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
source-map: 0.6.1
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
supports-color@5.5.0:
|
||||
dependencies:
|
||||
has-flag: 3.0.0
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
touch@3.1.1: {}
|
||||
|
||||
undefsafe@2.0.5: {}
|
||||
|
||||
ws@8.18.3: {}
|
||||
0
backend/pnpm-workspace.yaml
Normal file
0
backend/pnpm-workspace.yaml
Normal file
@@ -12,43 +12,22 @@ Debug Main Page
|
||||
</nav>
|
||||
<main class="dashboard">
|
||||
<section class="widget">
|
||||
<fieldset>
|
||||
<legend>Error reports</legend>
|
||||
<desc><a href="/dbg/error">CLICK HERE TO SEE THE ERROR REPORTS</a> </desc>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Profile Management</legend>
|
||||
<form method="post" action="/dbg/actions/resend-email-verification">
|
||||
<div class="row">
|
||||
<input type="email" name="email" placeholder="example@example.com" value="" />
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="force-verify">Are you sure?</label>
|
||||
<input id="force-verify" type="checkbox" name="force" />
|
||||
<br />
|
||||
<small>
|
||||
This is a just a security double check for prevent non intentional submits.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<input type="submit" name="resend" value="Resend Verification" />
|
||||
<input type="submit" name="verify" value="Verify" />
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<input type="submit" class="danger" name="block" value="Block" />
|
||||
<input type="submit" class="danger" name="unblock" value="Unblock" />
|
||||
</div>
|
||||
</form>
|
||||
<legend>CURRENT PROFILE</legend>
|
||||
<desc>
|
||||
<p>
|
||||
Name: <b>{{profile.fullname}}</b> <br />
|
||||
Email: <b>{{profile.email}}</b>
|
||||
</p>
|
||||
</desc>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>VIRTUAL CLOCK</legend>
|
||||
|
||||
<desc>
|
||||
<p><b>IMPORTANT:</b> The virtual clock is profile based and only affects the currently logged-in profile.</p>
|
||||
<p>
|
||||
CURRENT CLOCK: <b>{{current-clock}}</b>
|
||||
<br />
|
||||
@@ -81,8 +60,93 @@ Debug Main Page
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>ERROR REPORTS</legend>
|
||||
<desc><a href="/dbg/error">CLICK HERE TO SEE THE ERROR REPORTS</a> </desc>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="widget">
|
||||
<fieldset>
|
||||
<legend>Profile Management</legend>
|
||||
<form method="post" action="/dbg/actions/resend-email-verification">
|
||||
<div class="row">
|
||||
<input type="email" name="email" placeholder="example@example.com" value="" />
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="force-verify">Are you sure?</label>
|
||||
<input id="force-verify" type="checkbox" name="force" />
|
||||
<br />
|
||||
<small>
|
||||
This is a just a security double check for prevent non intentional submits.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<input type="submit" name="resend" value="Resend Verification" />
|
||||
<input type="submit" name="verify" value="Verify" />
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<input type="submit" class="danger" name="block" value="Block" />
|
||||
<input type="submit" class="danger" name="unblock" value="Unblock" />
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset>
|
||||
<legend>Feature Flags for Team</legend>
|
||||
<desc>Add a feature flag to a team</desc>
|
||||
<form method="post" action="/dbg/actions/handle-team-features">
|
||||
<div class="row">
|
||||
<input type="text" style="width:300px" name="team-id" placeholder="team-id" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<select type="text" style="width:100px" name="feature">
|
||||
{% for feature in supported-features %}
|
||||
<option value="{{feature}}">{{feature}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<select style="width:100px" name="action">
|
||||
<option value="">Action...</option>
|
||||
<option value="show">Show</option>
|
||||
<option value="enable">Enable</option>
|
||||
<option value="disable">Disable</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="check-feature">Skip feature check</label>
|
||||
<input id="check-feature" type="checkbox" name="skip-check" />
|
||||
<br />
|
||||
<small>
|
||||
Do not check if the feature is supported
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="force-version">Are you sure?</label>
|
||||
<input id="force-version" type="checkbox" name="force" />
|
||||
<br />
|
||||
<small>
|
||||
This is a just a security double check for prevent non intentional submits.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<input type="submit" value="Submit" />
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="widget">
|
||||
|
||||
<fieldset>
|
||||
@@ -173,55 +237,5 @@ Debug Main Page
|
||||
</form>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<section class="widget">
|
||||
<fieldset>
|
||||
<legend>Feature Flags for Team</legend>
|
||||
<desc>Add a feature flag to a team</desc>
|
||||
<form method="post" action="/dbg/actions/handle-team-features">
|
||||
<div class="row">
|
||||
<input type="text" style="width:300px" name="team-id" placeholder="team-id" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<select type="text" style="width:100px" name="feature">
|
||||
{% for feature in supported-features %}
|
||||
<option value="{{feature}}">{{feature}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<select style="width:100px" name="action">
|
||||
<option value="">Action...</option>
|
||||
<option value="show">Show</option>
|
||||
<option value="enable">Enable</option>
|
||||
<option value="disable">Disable</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="check-feature">Skip feature check</label>
|
||||
<input id="check-feature" type="checkbox" name="skip-check" />
|
||||
<br />
|
||||
<small>
|
||||
Do not check if the feature is supported
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="force-version">Are you sure?</label>
|
||||
<input id="force-version" type="checkbox" name="force" />
|
||||
<br />
|
||||
<small>
|
||||
This is a just a security double check for prevent non intentional submits.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<input type="submit" value="Submit" />
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@@ -13,6 +13,7 @@ export PENPOT_FLAGS="\
|
||||
disable-login-with-google \
|
||||
disable-login-with-github \
|
||||
disable-login-with-gitlab \
|
||||
disable-telemetry \
|
||||
enable-backend-worker \
|
||||
enable-backend-asserts \
|
||||
disable-feature-fdata-pointer-map \
|
||||
@@ -55,6 +56,8 @@ export PENPOT_OBJECTS_STORAGE_BACKEND=s3
|
||||
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
|
||||
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
|
||||
|
||||
export PENPOT_NITRATE_BACKEND_URI=http://localhost:3000/control-center
|
||||
|
||||
export JAVA_OPTS="\
|
||||
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
|
||||
-Djdk.attach.allowAttachSelf \
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
SCRIPT_DIR=$(dirname $0);
|
||||
source $SCRIPT_DIR/_env;
|
||||
|
||||
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||
source $SCRIPT_DIR/_env.local;
|
||||
fi
|
||||
|
||||
# Initialize MINIO config
|
||||
setup_minio;
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
SCRIPT_DIR=$(dirname $0);
|
||||
|
||||
source $SCRIPT_DIR/_env;
|
||||
|
||||
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||
source $SCRIPT_DIR/_env.local;
|
||||
fi
|
||||
|
||||
export OPTIONS="-A:dev"
|
||||
|
||||
entrypoint=${1:-app.main};
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
SCRIPT_DIR=$(dirname $0);
|
||||
source $SCRIPT_DIR/_env;
|
||||
|
||||
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||
source $SCRIPT_DIR/_env.local;
|
||||
fi
|
||||
|
||||
# Initialize MINIO config
|
||||
setup_minio;
|
||||
|
||||
|
||||
@@ -873,11 +873,8 @@
|
||||
(import-storage-objects cfg)
|
||||
|
||||
(let [files (get manifest :files)
|
||||
result (reduce (fn [result {:keys [id] :as file}]
|
||||
result (reduce (fn [result file]
|
||||
(let [name' (get file :name)
|
||||
name' (if (map? name)
|
||||
(get name id)
|
||||
name')
|
||||
file (assoc file :name name')]
|
||||
(conj result (import-file cfg file))))
|
||||
[]
|
||||
|
||||
@@ -225,6 +225,8 @@
|
||||
[:netty-io-threads {:optional true} ::sm/int]
|
||||
[:executor-threads {:optional true} ::sm/int]
|
||||
|
||||
[:nitrate-backend-uri {:optional true} ::sm/uri]
|
||||
|
||||
;; DEPRECATED
|
||||
[:assets-storage-backend {:optional true} :keyword]
|
||||
[:storage-assets-fs-directory {:optional true} :string]
|
||||
|
||||
@@ -49,13 +49,16 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn index-handler
|
||||
[_cfg _request]
|
||||
(let [{:keys [clock offset]} @clock/current]
|
||||
[cfg request]
|
||||
(let [profile-id (::session/profile-id request)
|
||||
offset (clock/get-offset profile-id)
|
||||
profile (profile/get-profile cfg profile-id)]
|
||||
{::yres/status 200
|
||||
::yres/headers {"content-type" "text/html"}
|
||||
::yres/body (-> (io/resource "app/templates/debug.tmpl")
|
||||
(tmpl/render {:version (:full cf/version)
|
||||
:current-clock (str clock)
|
||||
:profile profile
|
||||
:current-clock ct/*clock*
|
||||
:current-offset (if offset
|
||||
(ct/format-duration offset)
|
||||
"NO OFFSET")
|
||||
@@ -447,15 +450,16 @@
|
||||
|
||||
(defn- set-virtual-clock
|
||||
[_ {:keys [params] :as request}]
|
||||
(let [offset (some-> params :offset str/trim not-empty ct/duration)
|
||||
reset? (contains? params :reset)]
|
||||
(let [offset (some-> params :offset str/trim not-empty ct/duration)
|
||||
profile-id (::session/profile-id request)
|
||||
reset? (contains? params :reset)]
|
||||
(if (= "production" (cf/get :tenant))
|
||||
{::yres/status 501
|
||||
::yres/body "OPERATION NOT ALLOWED"}
|
||||
(do
|
||||
(if (or reset? (zero? (inst-ms offset)))
|
||||
(clock/set-offset! nil)
|
||||
(clock/set-offset! offset))
|
||||
(clock/assign-offset profile-id nil)
|
||||
(clock/assign-offset profile-id offset))
|
||||
{::yres/status 302
|
||||
::yres/headers {"location" "/dbg"}}))))
|
||||
|
||||
@@ -495,7 +499,7 @@
|
||||
|
||||
(defn authorized?
|
||||
[pool {:keys [::session/profile-id]}]
|
||||
(or (= "devenv" (cf/get :host))
|
||||
(or (and (= "devenv" (cf/get :host)) profile-id)
|
||||
(let [profile (ex/ignoring (profile/get-profile pool profile-id))
|
||||
admins (or (cf/get :admins) #{})]
|
||||
(contains? admins (:email profile)))))
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
[app.http.session.tasks :as-alias tasks]
|
||||
[app.main :as-alias main]
|
||||
[app.setup :as-alias setup]
|
||||
[app.setup.clock :as clock]
|
||||
[app.tokens :as tokens]
|
||||
[integrant.core :as ig]
|
||||
[yetti.request :as yreq]
|
||||
@@ -229,18 +230,22 @@
|
||||
(let [{:keys [type token claims metadata]} (get request ::http/auth-data)]
|
||||
(cond
|
||||
(= type :cookie)
|
||||
(let [session (case (:ver metadata)
|
||||
;; BACKWARD COMPATIBILITY WITH OLD TOKENS
|
||||
0 (read-session manager token)
|
||||
1 (some->> (:sid claims) (read-session manager))
|
||||
nil)
|
||||
(let [session
|
||||
(case (:ver metadata)
|
||||
;; BACKWARD COMPATIBILITY WITH OLD TOKENS
|
||||
0 (read-session manager token)
|
||||
1 (some->> (:sid claims) (read-session manager))
|
||||
nil)
|
||||
|
||||
request (cond-> request
|
||||
(some? session)
|
||||
(-> (assoc ::profile-id (:profile-id session))
|
||||
(assoc ::session session)))
|
||||
request
|
||||
(cond-> request
|
||||
(some? session)
|
||||
(-> (assoc ::profile-id (:profile-id session))
|
||||
(assoc ::session session)))
|
||||
|
||||
response (handler request)]
|
||||
response
|
||||
(binding [ct/*clock* (clock/get-clock (:profile-id session))]
|
||||
(handler request))]
|
||||
|
||||
(if (and session (renew-session? session))
|
||||
(let [session (->> session
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
(ns app.http.sse
|
||||
"SSE (server sent events) helpers"
|
||||
(:refer-clojure :exclude [tap])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.logging :as l]
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.json :as json]
|
||||
[app.common.logging :as l]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.time :as ct]
|
||||
@@ -223,7 +224,7 @@
|
||||
(some? tnow)
|
||||
(assoc :tracked-at tnow))))
|
||||
|
||||
(defn- append-audit-entry!
|
||||
(defn- append-audit-entry
|
||||
[cfg params]
|
||||
(let [params (-> params
|
||||
(update :props db/tjson)
|
||||
@@ -236,6 +237,16 @@
|
||||
(let [params (event->params event)
|
||||
tnow (ct/now)]
|
||||
|
||||
(when (contains? cf/flags :audit-log-logger)
|
||||
(l/log! ::l/logger "app.audit"
|
||||
::l/level :info
|
||||
:profile-id (str (::profile-id event))
|
||||
:ip-addr (str (::ip-addr event))
|
||||
:type (::type event)
|
||||
:name (::name event)
|
||||
:props (json/encode (::props event) :key-fn json/write-camel-key)
|
||||
:context (json/encode (::context event) :key-fn json/write-camel-key)))
|
||||
|
||||
(when (contains? cf/flags :audit-log)
|
||||
;; NOTE: this operation may cause primary key conflicts on inserts
|
||||
;; because of the timestamp precission (two concurrent requests), in
|
||||
@@ -243,7 +254,7 @@
|
||||
(let [params (-> params
|
||||
(assoc :created-at tnow)
|
||||
(update :tracked-at #(or % tnow)))]
|
||||
(append-audit-entry! cfg params)))
|
||||
(append-audit-entry cfg params)))
|
||||
|
||||
(when (and (or (contains? cf/flags :telemetry)
|
||||
(cf/get :telemetry-enabled))
|
||||
@@ -258,7 +269,7 @@
|
||||
(update :tracked-at #(or % tnow))
|
||||
(assoc :props {})
|
||||
(assoc :context {}))]
|
||||
(append-audit-entry! cfg params)))
|
||||
(append-audit-entry cfg params)))
|
||||
|
||||
(when (and (contains? cf/flags :webhooks)
|
||||
(::webhooks/event? event))
|
||||
@@ -312,4 +323,4 @@
|
||||
params (-> (event->params event)
|
||||
(assoc :created-at tnow)
|
||||
(update :tracked-at #(or % tnow)))]
|
||||
(append-audit-entry! cfg params)))))))
|
||||
(append-audit-entry cfg params)))))))
|
||||
|
||||
@@ -323,6 +323,7 @@
|
||||
{::http.client/client (ig/ref ::http.client/client)
|
||||
::db/pool (ig/ref ::db/pool)
|
||||
::rds/pool (ig/ref ::rds/pool)
|
||||
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||
::wrk/executor (ig/ref ::wrk/netty-executor)
|
||||
::session/manager (ig/ref ::session/manager)
|
||||
::ldap/provider (ig/ref ::ldap/provider)
|
||||
@@ -339,6 +340,9 @@
|
||||
::email/blacklist (ig/ref ::email/blacklist)
|
||||
::email/whitelist (ig/ref ::email/whitelist)}
|
||||
|
||||
:app.nitrate/client
|
||||
{::http.client/client (ig/ref ::http.client/client)}
|
||||
|
||||
:app.rpc/management-methods
|
||||
{::http.client/client (ig/ref ::http.client/client)
|
||||
::db/pool (ig/ref ::db/pool)
|
||||
@@ -348,6 +352,7 @@
|
||||
::sto/storage (ig/ref ::sto/storage)
|
||||
::mtx/metrics (ig/ref ::mtx/metrics)
|
||||
::mbus/msgbus (ig/ref ::mbus/msgbus)
|
||||
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||
::rds/client (ig/ref ::rds/client)
|
||||
::setup/props (ig/ref ::setup/props)}
|
||||
|
||||
|
||||
147
backend/src/app/nitrate.clj
Normal file
147
backend/src/app/nitrate.clj
Normal file
@@ -0,0 +1,147 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.nitrate
|
||||
"Module that make calls to the external nitrate aplication"
|
||||
(:require
|
||||
[app.common.logging :as l]
|
||||
[app.common.schema :as sm]
|
||||
[app.config :as cf]
|
||||
[app.http.client :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.setup :as-alias setup]
|
||||
[app.util.json :as json]
|
||||
[clojure.core :as c]
|
||||
[integrant.core :as ig]))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; HELPERS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- request-builder
|
||||
[cfg method uri management-key profile-id]
|
||||
(fn []
|
||||
(http/req! cfg {:method method
|
||||
:headers {"content-type" "application/json"
|
||||
"accept" "application/json"
|
||||
"x-shared-key" management-key
|
||||
"x-profile-id" (str profile-id)}
|
||||
:uri uri
|
||||
:version :http1.1})))
|
||||
|
||||
|
||||
(defn- with-retries
|
||||
[handler max-retries]
|
||||
(fn []
|
||||
(loop [attempt 1]
|
||||
(let [result (try
|
||||
(handler)
|
||||
(catch Exception e
|
||||
(if (< attempt max-retries)
|
||||
::retry
|
||||
(do
|
||||
;; TODO Error handling
|
||||
(l/error :hint "request fail after multiple retries" :cause e)
|
||||
nil))))]
|
||||
(if (= result ::retry)
|
||||
(recur (inc attempt))
|
||||
result)))))
|
||||
|
||||
|
||||
(defn- with-validate [handler uri schema]
|
||||
(fn []
|
||||
(let [coercer-http (sm/coercer schema
|
||||
:type :validation
|
||||
:hint (str "invalid data received calling " uri))]
|
||||
(try
|
||||
(coercer-http (-> (handler) :body json/decode))
|
||||
(catch Exception e
|
||||
;; TODO Error handling
|
||||
(l/error :hint "error validating json response" :cause e)
|
||||
nil)))))
|
||||
|
||||
(defn- request-to-nitrate
|
||||
[{:keys [::management-key] :as cfg} method uri schema {:keys [::rpc/profile-id] :as params}]
|
||||
(let [full-http-call (-> (request-builder cfg method uri management-key profile-id)
|
||||
(with-retries 3)
|
||||
(with-validate uri schema))]
|
||||
(full-http-call)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; API
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn call
|
||||
[cfg method params]
|
||||
(when (contains? cf/flags :nitrate)
|
||||
(let [client (get cfg ::client)
|
||||
method (get client method)]
|
||||
(method params))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:private schema:organization
|
||||
[:map
|
||||
[:id ::sm/text]
|
||||
[:name ::sm/text]])
|
||||
|
||||
(def ^:private schema:user
|
||||
[:map
|
||||
[:valid ::sm/boolean]])
|
||||
|
||||
(defn- get-team-org
|
||||
[cfg {:keys [team-id] :as params}]
|
||||
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||
(request-to-nitrate cfg :get (str baseuri "/api/teams/" (str team-id)) schema:organization params)))
|
||||
|
||||
(defn- is-valid-user
|
||||
[cfg {:keys [profile-id] :as params}]
|
||||
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||
(request-to-nitrate cfg :get (str baseuri "/api/users/" (str profile-id)) schema:user params)))
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; INITIALIZATION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmethod ig/init-key ::client
|
||||
[_ {:keys [::setup/props] :as cfg}]
|
||||
(if (contains? cf/flags :nitrate)
|
||||
(let [management-key (or (cf/get :management-api-key)
|
||||
(get props :management-key))
|
||||
cfg (assoc cfg ::management-key management-key)]
|
||||
{:get-team-org (partial get-team-org cfg)
|
||||
:is-valid-user (partial is-valid-user cfg)})
|
||||
{}))
|
||||
|
||||
(defmethod ig/halt-key! ::client
|
||||
[_ {:keys []}]
|
||||
(do :stuff))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; UTILS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(defn add-nitrate-licence-to-profile
|
||||
[cfg profile]
|
||||
(try
|
||||
(let [nitrate-licence (call cfg :is-valid-user {:profile-id (:id profile)})]
|
||||
(assoc profile :nitrate-licence (:valid nitrate-licence)))
|
||||
(catch Throwable cause
|
||||
(l/error :hint "failed to get nitrate licence"
|
||||
:profile-id (:id profile)
|
||||
:cause cause)
|
||||
profile)))
|
||||
|
||||
(defn add-org-to-team
|
||||
[cfg team params]
|
||||
(let [params (assoc (or params {}) :team-id (:id team))
|
||||
org (call cfg :get-team-org params)]
|
||||
(assoc team :organization-id (:id org) :organization-name (:name org))))
|
||||
@@ -301,6 +301,7 @@
|
||||
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)]
|
||||
(->> (sv/scan-ns
|
||||
'app.rpc.management.subscription
|
||||
'app.rpc.management.nitrate
|
||||
'app.rpc.management.exporter)
|
||||
(map (partial process-method cfg "management" wrap-management))
|
||||
(into {}))))
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
[app.loggers.audit :as audit]
|
||||
[app.main :as-alias main]
|
||||
[app.media :as media]
|
||||
[app.nitrate :as nitrate]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as climit]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
@@ -88,6 +89,8 @@
|
||||
|
||||
;; --- QUERY: Get profile (own)
|
||||
|
||||
|
||||
|
||||
(sv/defmethod ::get-profile
|
||||
{::rpc/auth false
|
||||
::doc/added "1.18"
|
||||
@@ -98,9 +101,13 @@
|
||||
;; no profile-id is in session, and when db call raises not found. In all other
|
||||
;; cases we need to reraise the exception.
|
||||
(try
|
||||
(-> (get-profile pool profile-id)
|
||||
(strip-private-attrs)
|
||||
(update :props filter-props))
|
||||
(let [profile (-> (get-profile pool profile-id)
|
||||
(strip-private-attrs)
|
||||
(update :props filter-props))]
|
||||
(if (contains? cf/flags :nitrate)
|
||||
(nitrate/add-nitrate-licence-to-profile cfg profile)
|
||||
profile))
|
||||
|
||||
(catch Throwable _
|
||||
{:id uuid/zero :fullname "Anonymous User"})))
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
[app.main :as-alias main]
|
||||
[app.media :as media]
|
||||
[app.msgbus :as mbus]
|
||||
[app.nitrate :as nitrate]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.profile :as profile]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
@@ -190,7 +191,9 @@
|
||||
::sm/params schema:get-teams}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
||||
(dm/with-open [conn (db/open pool)]
|
||||
(get-teams conn profile-id)))
|
||||
(cond->> (get-teams conn profile-id)
|
||||
(contains? cf/flags :nitrate)
|
||||
(map #(nitrate/add-org-to-team cfg % params)))))
|
||||
|
||||
(def ^:private sql:get-owned-teams
|
||||
"SELECT t.id, t.name,
|
||||
|
||||
@@ -248,11 +248,11 @@
|
||||
|
||||
invitations (into #{}
|
||||
(comp
|
||||
;; We don't re-send invitations to
|
||||
;; already existing members
|
||||
;; We don't re-send invitations to
|
||||
;; already existing members
|
||||
(remove #(contains? team-members (:email %)))
|
||||
;; We don't send invitations to
|
||||
;; join-requested members
|
||||
;; We don't send invitations to
|
||||
;; join-requested members
|
||||
(remove #(contains? join-requests (:email %)))
|
||||
(map (fn [{:keys [email role]}]
|
||||
(create-invitation cfg
|
||||
|
||||
112
backend/src/app/rpc/management/nitrate.clj
Normal file
112
backend/src/app/rpc/management/nitrate.clj
Normal file
@@ -0,0 +1,112 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.rpc.management.nitrate
|
||||
"Internal Nitrate HTTP API.
|
||||
Provides authenticated access to organization management and token validation endpoints.
|
||||
All requests must include a valid shared key token in the `x-shared-key` header, and
|
||||
a cookie `auth-token` with the user token.
|
||||
They will return `401 Unauthorized` if the shared key or user token are invalid."
|
||||
(:require
|
||||
[app.common.schema :as sm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.msgbus :as mbus]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.commands.profile :as profile]
|
||||
[app.rpc.doc :as doc]
|
||||
[app.util.services :as sv]))
|
||||
|
||||
;; ---- API: authenticate
|
||||
(def ^:private schema:profile
|
||||
[:map
|
||||
[:id ::sm/uuid]
|
||||
[:name :string]
|
||||
[:email :string]
|
||||
[:photo-url :string]])
|
||||
|
||||
(sv/defmethod ::authenticate
|
||||
"Authenticate an user
|
||||
@api GET /authenticate
|
||||
@returns
|
||||
200 OK: Returns the authenticated user."
|
||||
{::doc/added "2.12"
|
||||
::sm/result schema:profile}
|
||||
[cfg {:keys [::rpc/profile-id] :as params}]
|
||||
(let [profile (profile/get-profile cfg profile-id)]
|
||||
{:id (get profile :id)
|
||||
:name (get profile :fullname)
|
||||
:email (get profile :email)
|
||||
:photo-url (files/resolve-public-uri (get profile :photo-id))}))
|
||||
|
||||
;; ---- API: get-teams
|
||||
|
||||
(def ^:private sql:get-teams
|
||||
"SELECT t.*
|
||||
FROM team AS t
|
||||
JOIN team_profile_rel AS tpr ON t.id = tpr.team_id
|
||||
WHERE tpr.profile_id = ?
|
||||
AND tpr.is_owner = 't'
|
||||
AND t.is_default = 'f'
|
||||
AND t.deleted_at is null;")
|
||||
|
||||
(def ^:private schema:team
|
||||
[:map
|
||||
[:id ::sm/uuid]
|
||||
[:name :string]])
|
||||
|
||||
(def ^:private schema:get-teams-result
|
||||
[:vector schema:team])
|
||||
|
||||
(sv/defmethod ::get-teams
|
||||
"List teams for which current user is owner.
|
||||
@api GET /get-teams
|
||||
@returns
|
||||
200 OK: Returns the list of teams for the user."
|
||||
{::doc/added "2.12"
|
||||
::sm/result schema:get-teams-result}
|
||||
[cfg {:keys [::rpc/profile-id]}]
|
||||
(when (contains? cf/flags :nitrate)
|
||||
(let [current-user-id (-> (profile/get-profile cfg profile-id) :id)]
|
||||
(->> (db/exec! cfg [sql:get-teams current-user-id])
|
||||
(map #(select-keys % [:id :name]))))))
|
||||
|
||||
;; ---- API: notify-team-change
|
||||
|
||||
(def ^:private schema:notify-team-change
|
||||
[:map
|
||||
[:id ::sm/uuid]
|
||||
[:organization-id ::sm/text]])
|
||||
|
||||
|
||||
(sv/defmethod ::notify-team-change
|
||||
"Notify to Penpot a team change from nitrate
|
||||
@api POST /notify-team-change
|
||||
@returns
|
||||
200 OK"
|
||||
{::doc/added "2.12"
|
||||
::sm/params schema:notify-team-change
|
||||
::rpc/auth false}
|
||||
[cfg {:keys [id organization-id organization-name]}]
|
||||
(when (contains? cf/flags :nitrate)
|
||||
(let [msgbus (::mbus/msgbus cfg)]
|
||||
(mbus/pub! msgbus
|
||||
;;TODO There is a bug on dashboard with teams notifications.
|
||||
;;For now we send it to uuid/zero instead of team-id
|
||||
:topic uuid/zero
|
||||
:message {:type :team-org-change
|
||||
:team-id id
|
||||
:organization-id organization-id
|
||||
:organization-name organization-name}))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,48 +9,35 @@
|
||||
modification of time offset (useful for testing and time adjustments)."
|
||||
(:require
|
||||
[app.common.logging :as l]
|
||||
[app.common.time :as ct]
|
||||
[app.setup :as-alias setup]
|
||||
[integrant.core :as ig])
|
||||
(:import
|
||||
java.time.Clock
|
||||
java.time.Duration
|
||||
java.time.Instant
|
||||
java.time.ZoneId))
|
||||
[app.common.time :as ct]))
|
||||
|
||||
(defonce current
|
||||
(atom {:clock (Clock/systemDefaultZone)
|
||||
:offset nil}))
|
||||
(defonce state
|
||||
(atom {}))
|
||||
|
||||
(defmethod ig/init-key ::setup/clock
|
||||
[_ _]
|
||||
(add-watch current ::common
|
||||
(fn [_ _ _ {:keys [clock offset]}]
|
||||
(let [clock (if (ct/duration? offset)
|
||||
(Clock/offset ^Clock clock
|
||||
^Duration offset)
|
||||
clock)]
|
||||
(l/wrn :hint "altering clock" :clock (str clock))
|
||||
(alter-var-root #'ct/*clock* (constantly clock))))))
|
||||
(defn assign-offset
|
||||
"Assign virtual clock offset to a specific user. Is the responsability
|
||||
of RPC module to properly bind the correct clock to the user
|
||||
request."
|
||||
[profile-id duration]
|
||||
(swap! state (fn [state]
|
||||
(if (nil? duration)
|
||||
(dissoc state profile-id)
|
||||
(assoc state profile-id duration)))))
|
||||
|
||||
(defn get-offset
|
||||
[profile-id]
|
||||
(get @state profile-id))
|
||||
|
||||
(defmethod ig/halt-key! ::setup/clock
|
||||
[_ _]
|
||||
(remove-watch current ::common))
|
||||
(defn get-clock
|
||||
[profile-id]
|
||||
(if-let [offset (get-offset profile-id)]
|
||||
(ct/offset-clock offset)
|
||||
(ct/get-system-clock)))
|
||||
|
||||
(defn fixed
|
||||
"Get fixed clock, mainly used in tests"
|
||||
[instant]
|
||||
(Clock/fixed ^Instant (ct/inst instant)
|
||||
^ZoneId (ZoneId/of "Z")))
|
||||
|
||||
(defn set-offset!
|
||||
[duration]
|
||||
(swap! current assoc :offset (some-> duration ct/duration)))
|
||||
|
||||
(defn set-clock!
|
||||
(defn set-global-clock
|
||||
([]
|
||||
(swap! current assoc :clock (Clock/systemDefaultZone)))
|
||||
(set-global-clock (ct/get-system-clock)))
|
||||
([clock]
|
||||
(when (instance? Clock clock)
|
||||
(swap! current assoc :clock clock))))
|
||||
(assert (ct/clock? clock) "expected valid clock instance")
|
||||
(l/wrn :hint "altering clock" :clock (str clock))
|
||||
(alter-var-root #'ct/*clock* (constantly clock))))
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"A generic asynchronous events notifications subsystem; used mainly
|
||||
for mark event points in functions and be able to attach listeners
|
||||
to them. Mainly used in http.sse for progress reporting."
|
||||
(:refer-clojure :exclude [tap run!])
|
||||
(:refer-clojure :exclude [run!])
|
||||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.logging :as l]
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
[app.db.sql :as sql]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.setup.clock :as clock]
|
||||
[app.storage :as sto]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]
|
||||
@@ -134,7 +133,7 @@
|
||||
;; this will run pending task triggered by deleting user snapshot
|
||||
(th/run-pending-tasks!)
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
;; delete 2 snapshots and 2 file data entries
|
||||
(t/is (= 4 (:processed res)))))))))
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.setup.clock :as clock]
|
||||
[app.storage :as sto]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]
|
||||
@@ -842,7 +841,7 @@
|
||||
out (th/command! data)
|
||||
error (:error out)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (th/ex-of-type? error :not-found))))
|
||||
|
||||
@@ -864,7 +863,7 @@
|
||||
out (th/command! data)
|
||||
error (:error out)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (th/ex-of-type? error :not-found))))
|
||||
|
||||
@@ -922,7 +921,7 @@
|
||||
(t/is (= 0 (:processed result))))
|
||||
|
||||
;; run permanent deletion
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 3 (:processed result)))))
|
||||
|
||||
@@ -1262,7 +1261,7 @@
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (every? #(some? (:data %)) rows)))
|
||||
|
||||
;; Mark the file ellegible again for GC
|
||||
;; Mark the file ellegible again for GC
|
||||
(th/db-update! :file
|
||||
{:has-media-trimmed false}
|
||||
{:id (:id file)})
|
||||
@@ -1319,7 +1318,7 @@
|
||||
{:file-id (:id file)
|
||||
:type "fragment"}
|
||||
{:order-by [:created-at]})]
|
||||
;; (pp/pprint rows)
|
||||
;; (pp/pprint rows)
|
||||
(t/is (= 2 (count rows)))
|
||||
(t/is (nil? (:data row1)))
|
||||
(t/is (= "storage" (:backend row1)))
|
||||
@@ -1875,7 +1874,7 @@
|
||||
file-id (uuid/next)
|
||||
now (ct/inst "2025-10-31T00:00:00Z")]
|
||||
|
||||
(binding [ct/*clock* (clock/fixed now)]
|
||||
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||
(let [data {::th/type :create-file
|
||||
::rpc/profile-id (:id prof)
|
||||
:project-id proj-id
|
||||
@@ -1937,7 +1936,7 @@
|
||||
file-id (uuid/next)
|
||||
now (ct/inst "2025-10-31T00:00:00Z")]
|
||||
|
||||
(binding [ct/*clock* (clock/fixed now)]
|
||||
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||
(let [data {::th/type :create-file
|
||||
::rpc/profile-id (:id prof)
|
||||
:project-id proj-id
|
||||
@@ -2000,7 +1999,7 @@
|
||||
team-id (:default-team-id profile)
|
||||
now (ct/inst "2025-10-31T00:00:00Z")]
|
||||
|
||||
(binding [ct/*clock* (clock/fixed now)]
|
||||
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||
(let [project (th/create-project* 1 {:profile-id (:id profile)
|
||||
:team-id team-id})
|
||||
file (th/create-file* 1 {:profile-id (:id profile)
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
(t/is (map? (:result out))))
|
||||
|
||||
;; run the task again
|
||||
(let [res (binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 31}))]
|
||||
(let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 31}))]
|
||||
(th/run-task! "storage-gc-touched" {}))]
|
||||
(t/is (= 2 (:freeze res))))
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
(t/is (some? (sto/get-object storage (:media-id row2))))
|
||||
|
||||
;; run the task again
|
||||
(let [res (binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 31}))]
|
||||
(let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 31}))]
|
||||
(th/run-task! :storage-gc-touched {}))]
|
||||
(t/is (= 1 (:delete res)))
|
||||
(t/is (= 0 (:freeze res))))
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
;; Run the storage gc deleted task, it should permanently delete
|
||||
;; all storage objects related to the deleted thumbnails
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 1 (:deleted res)))))
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
|
||||
;; Run the storage gc deleted task, it should permanently delete
|
||||
;; all storage objects related to the deleted thumbnails
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [result (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 1 (:deleted result)))))
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.setup.clock :as clock]
|
||||
[app.storage :as sto]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]
|
||||
@@ -147,7 +146,7 @@
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res))))
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
@@ -208,7 +207,7 @@
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res))))
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 1 (:processed res))))
|
||||
|
||||
@@ -268,7 +267,7 @@
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res))))
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 1 (:processed res))))
|
||||
|
||||
|
||||
@@ -536,7 +536,7 @@
|
||||
:token rtoken}
|
||||
|
||||
{:keys [result error] :as out} (th/command! data)]
|
||||
;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? error))
|
||||
(t/is (map? result))
|
||||
(t/is (string? (:invitation-token result))))))
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.setup.clock :as clock]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]))
|
||||
|
||||
@@ -31,7 +30,7 @@
|
||||
:team-id (:id team)
|
||||
:name "test project"}
|
||||
out (th/command! data)]
|
||||
;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
|
||||
(t/is (nil? (:error out)))
|
||||
(let [result (:result out)]
|
||||
@@ -94,7 +93,7 @@
|
||||
:id project-id}
|
||||
out (th/command! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out))))
|
||||
|
||||
@@ -228,7 +227,7 @@
|
||||
(t/is (= 0 (count result)))))
|
||||
|
||||
;; run permanent deletion
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 1 (:processed result)))))
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.setup.clock :as clock]
|
||||
[app.storage :as sto]
|
||||
[app.tokens :as tokens]
|
||||
[backend-tests.helpers :as th]
|
||||
@@ -526,7 +525,7 @@
|
||||
(t/is (= :not-found (:type edata)))))
|
||||
|
||||
;; run permanent deletion
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed result)))))
|
||||
|
||||
@@ -583,7 +582,7 @@
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (ct/inst? (:deleted-at (first rows)))))
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 7 (:processed result)))))))
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.setup.clock :as clock]
|
||||
[app.storage :as sto]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]
|
||||
@@ -99,14 +98,14 @@
|
||||
::sto/expired-at (ct/in-future {:hours 1})
|
||||
:content-type "text/plain"})]
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 0}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 0}))]
|
||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 1 (:deleted res)))))
|
||||
|
||||
(let [res (th/db-exec-one! ["select count(*) from storage_object;"])]
|
||||
(t/is (= 2 (:count res))))
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 61}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 61}))]
|
||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 1 (:deleted res)))))
|
||||
|
||||
@@ -331,22 +330,22 @@
|
||||
:content-type "text/plain"})]
|
||||
|
||||
|
||||
(binding [ct/*clock* (clock/fixed now)]
|
||||
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||
(let [res (th/run-task! :storage-gc-touched {})]
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res)))))
|
||||
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:minutes 1}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/plus now {:minutes 1}))]
|
||||
(let [res (th/run-task! :storage-gc-touched {})]
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 1 (:delete res)))))
|
||||
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:hours 1}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/plus now {:hours 1}))]
|
||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 0 (:deleted res)))))
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:hours 2}))]
|
||||
(binding [ct/*clock* (ct/fixed-clock (ct/plus now {:hours 2}))]
|
||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 0 (:deleted res)))))))
|
||||
|
||||
1145
backend/yarn.lock
1145
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
7
common/.gitignore
vendored
7
common/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
@@ -59,7 +59,7 @@
|
||||
thheller/shadow-cljs {:mvn/version "3.2.0"}
|
||||
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
|
||||
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
|
||||
criterium/criterium {:mvn/version "RELEASE"}
|
||||
criterium/criterium {:mvn/version "0.4.6"}
|
||||
mockery/mockery {:mvn/version "RELEASE"}}
|
||||
:extra-paths ["test" "dev"]}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
|
||||
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -23,9 +23,9 @@
|
||||
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
|
||||
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
||||
"lint:clj": "clj-kondo --parallel=true --lint src/",
|
||||
"lint": "yarn run lint:clj",
|
||||
"lint": "pnpm run lint:clj",
|
||||
"watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests/ --exec 'node target/tests/test.js'\"",
|
||||
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
||||
"test": "yarn run build:test && node target/tests/test.js"
|
||||
"test": "pnpm run build:test && node target/tests/test.js"
|
||||
}
|
||||
}
|
||||
|
||||
489
common/pnpm-lock.yaml
generated
Normal file
489
common/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,489 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
date-fns:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
devDependencies:
|
||||
concurrently:
|
||||
specifier: ^9.1.2
|
||||
version: 9.2.1
|
||||
nodemon:
|
||||
specifier: ^3.1.10
|
||||
version: 3.1.11
|
||||
source-map-support:
|
||||
specifier: ^0.5.21
|
||||
version: 0.5.21
|
||||
ws:
|
||||
specifier: ^8.18.2
|
||||
version: 8.18.3
|
||||
|
||||
packages:
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||
|
||||
braces@3.0.3:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
||||
cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
concurrently@9.2.1:
|
||||
resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
date-fns@4.1.0:
|
||||
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
fill-range@7.1.1:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
has-flag@3.0.0:
|
||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
ignore-by-default@1.0.1:
|
||||
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
nodemon@3.1.11:
|
||||
resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
pstree.remy@1.1.8:
|
||||
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
||||
require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
rxjs@7.8.2:
|
||||
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||
|
||||
semver@7.7.3:
|
||||
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
shell-quote@1.8.3:
|
||||
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||
|
||||
source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
supports-color@5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
supports-color@8.1.1:
|
||||
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
touch@3.1.1:
|
||||
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
||||
hasBin: true
|
||||
|
||||
tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
undefsafe@2.0.5:
|
||||
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
ws@8.18.3:
|
||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yargs@17.7.2:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
snapshots:
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
concat-map: 0.0.1
|
||||
|
||||
braces@3.0.3:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
chalk@4.1.2:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
chokidar@3.6.0:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.3
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
cliui@8.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
color-convert@2.0.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
concurrently@9.2.1:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
rxjs: 7.8.2
|
||||
shell-quote: 1.8.3
|
||||
supports-color: 8.1.1
|
||||
tree-kill: 1.2.2
|
||||
yargs: 17.7.2
|
||||
|
||||
date-fns@4.1.0: {}
|
||||
|
||||
debug@4.4.3(supports-color@5.5.0):
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
optionalDependencies:
|
||||
supports-color: 5.5.0
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
fill-range@7.1.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
|
||||
has-flag@3.0.0: {}
|
||||
|
||||
has-flag@4.0.0: {}
|
||||
|
||||
ignore-by-default@1.0.1: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
|
||||
is-extglob@2.1.1: {}
|
||||
|
||||
is-fullwidth-code-point@3.0.0: {}
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
minimatch@3.1.2:
|
||||
dependencies:
|
||||
brace-expansion: 1.1.12
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nodemon@3.1.11:
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
debug: 4.4.3(supports-color@5.5.0)
|
||||
ignore-by-default: 1.0.1
|
||||
minimatch: 3.1.2
|
||||
pstree.remy: 1.1.8
|
||||
semver: 7.7.3
|
||||
simple-update-notifier: 2.0.0
|
||||
supports-color: 5.5.0
|
||||
touch: 3.1.1
|
||||
undefsafe: 2.0.5
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
pstree.remy@1.1.8: {}
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
|
||||
rxjs@7.8.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
semver@7.7.3: {}
|
||||
|
||||
shell-quote@1.8.3: {}
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
dependencies:
|
||||
semver: 7.7.3
|
||||
|
||||
source-map-support@0.5.21:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
source-map: 0.6.1
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
|
||||
supports-color@5.5.0:
|
||||
dependencies:
|
||||
has-flag: 3.0.0
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
supports-color@8.1.1:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
touch@3.1.1: {}
|
||||
|
||||
tree-kill@1.2.2: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
undefsafe@2.0.5: {}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
ws@8.18.3: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yargs@17.7.2:
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
escalade: 3.2.0
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
string-width: 4.2.3
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
0
common/pnpm-workspace.yaml
Normal file
0
common/pnpm-workspace.yaml
Normal file
@@ -3,5 +3,5 @@
|
||||
set -ex
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run test;
|
||||
pnpm install;
|
||||
pnpm run test;
|
||||
|
||||
@@ -1092,9 +1092,9 @@
|
||||
(if (number? num)
|
||||
(try
|
||||
(let [num-str (mth/to-fixed num precision)
|
||||
;; Remove all trailing zeros after the comma 100.00000
|
||||
;; Remove all trailing zeros after the comma 100.00000
|
||||
num-str (str/replace num-str trail-zeros-regex-1 "")]
|
||||
;; Remove trailing zeros after a decimal number: 0.001|00|
|
||||
;; Remove trailing zeros after a decimal number: 0.001|00|
|
||||
(if-let [m (re-find trail-zeros-regex-2 num-str)]
|
||||
(str/replace num-str (first m) (second m))
|
||||
num-str))
|
||||
|
||||
@@ -241,7 +241,20 @@
|
||||
(with-out-str
|
||||
(print-all cause)))))
|
||||
|
||||
#?(:clj
|
||||
(defn print-throwable
|
||||
[cause & {:as opts}]
|
||||
(println (format-throwable cause opts))))
|
||||
(defn print-throwable
|
||||
[cause & {:as opts}]
|
||||
#?(:clj
|
||||
(println (format-throwable cause opts))
|
||||
:cljs
|
||||
(let [prefix (get opts :prefix "exception")
|
||||
title (str prefix ": " (ex-message cause))
|
||||
exdata (ex-data cause)]
|
||||
(js/console.group title)
|
||||
(when-let [explain (get exdata ::sm/explain)]
|
||||
(println (sm/humanize-explain explain)))
|
||||
|
||||
(js/console.log "\nData:")
|
||||
(pp/pprint (dissoc exdata ::sm/explain))
|
||||
|
||||
(js/console.log "\nTrace:")
|
||||
(js/console.error (.-stack cause)))))
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Set parent to root frame.
|
||||
;; Set parent to root frame.
|
||||
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
||||
(assoc shape :parent-id uuid/zero))]
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [parent-shape]
|
||||
; Add shape to parent's children list
|
||||
;; Add shape to parent's children list
|
||||
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
||||
(update parent-shape :shapes conj (:id shape)))]
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Remove duplicated
|
||||
;; Remove duplicated
|
||||
(log/debug :hint " -> remove duplicated children")
|
||||
(update shape :shapes distinct))]
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Locate the first frame in parents and set frame-id to it.
|
||||
;; Locate the first frame in parents and set frame-id to it.
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||
frame-id (or (:id frame) uuid/zero)]
|
||||
@@ -118,7 +118,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Locate the first frame in parents and set frame-id to it.
|
||||
;; Locate the first frame in parents and set frame-id to it.
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||
frame-id (or (:id frame) uuid/zero)]
|
||||
@@ -134,7 +134,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Set the :shape as main instance root
|
||||
;; Set the :shape as main instance root
|
||||
(log/debug :hint " -> set :main-instance")
|
||||
(assoc shape :main-instance true))]
|
||||
|
||||
@@ -147,12 +147,13 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Set :component-file to local file
|
||||
;; Set :component-file to local file
|
||||
(log/debug :hint " -> set :component-file to local file")
|
||||
(assoc shape :component-file (:id file-data)))]
|
||||
; There is no solution that may recover it with confidence
|
||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
;; shape)]
|
||||
|
||||
;; There is no solution that may recover it with confidence
|
||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
;; shape)]
|
||||
|
||||
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
@@ -166,12 +167,12 @@
|
||||
|
||||
repair-shape
|
||||
(fn [shape]
|
||||
; Detach the shape and convert it to non instance.
|
||||
;; Detach the shape and convert it to non instance.
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
; There is no solution that may recover it with confidence
|
||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
;; shape)]
|
||||
;; There is no solution that may recover it with confidence
|
||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
;; shape)]
|
||||
|
||||
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
@@ -184,7 +185,7 @@
|
||||
|
||||
repair-component
|
||||
(fn [component]
|
||||
; Assign main instance in the component to current shape
|
||||
;; Assign main instance in the component to current shape
|
||||
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
||||
(assoc component :main-instance-id (:id shape)))
|
||||
|
||||
@@ -207,7 +208,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-component
|
||||
(fn [component]
|
||||
; Assign main instance in the component to current shape
|
||||
;; Assign main instance in the component to current shape
|
||||
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
||||
(assoc component :main-instance-page page-id))]
|
||||
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
@@ -219,7 +220,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; There is no solution that may recover it with confidence
|
||||
;; There is no solution that may recover it with confidence
|
||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
shape)]
|
||||
|
||||
@@ -232,7 +233,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Unset the :shape as main instance root
|
||||
;; Unset the :shape as main instance root
|
||||
(log/debug :hint " -> unset :main-instance")
|
||||
(dissoc shape :main-instance))]
|
||||
|
||||
@@ -245,7 +246,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a top copy root.
|
||||
;; Convert the shape in a top copy root.
|
||||
(log/debug :hint " -> set :component-root")
|
||||
(assoc shape :component-root true))]
|
||||
|
||||
@@ -258,7 +259,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a nested copy root.
|
||||
;; Convert the shape in a nested copy root.
|
||||
(log/debug :hint " -> unset :component-root")
|
||||
(dissoc shape :component-root))]
|
||||
|
||||
@@ -307,8 +308,8 @@
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
|
||||
; If the shape still refers to the remote component, try to find the corresponding near one
|
||||
; and link to it. If not, detach the shape.
|
||||
;; If the shape still refers to the remote component, try to find the corresponding near one
|
||||
;; and link to it. If not, detach the shape.
|
||||
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(if (some? matching-shape)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
@@ -324,7 +325,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert shape in a normal copy, removing nested copy status
|
||||
;; Convert shape in a normal copy, removing nested copy status
|
||||
(log/debug :hint " -> unhead shape")
|
||||
(ctk/unhead-shape shape))]
|
||||
|
||||
@@ -337,7 +338,7 @@
|
||||
[_ {:keys [shape page-id args] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert shape in a nested head, adding component info
|
||||
;; Convert shape in a nested head, adding component info
|
||||
(log/debug :hint " -> reroot shape")
|
||||
(ctk/rehead-shape shape (:component-file args) (:component-id args)))]
|
||||
|
||||
@@ -350,8 +351,9 @@
|
||||
[_ {:keys [shape args] :as error} file-data _]
|
||||
(let [repair-component
|
||||
(fn [component]
|
||||
(let [objects (:objects component) ;; we only have encounter this on deleted components,
|
||||
;; so the relevant objects are inside the component
|
||||
(let [objects (:objects component)
|
||||
;; we only have encounter this on deleted components,
|
||||
;; so the relevant objects are inside the component
|
||||
to-detach (->> (:cycles-ids args)
|
||||
(map #(get objects %))
|
||||
(map #(ctn/get-head-shape objects %))
|
||||
@@ -378,7 +380,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Remove shape-ref
|
||||
;; Remove shape-ref
|
||||
(log/debug :hint " -> unset :shape-ref")
|
||||
(dissoc shape :shape-ref))]
|
||||
|
||||
@@ -391,7 +393,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a nested main head.
|
||||
;; Convert the shape in a nested main head.
|
||||
(log/debug :hint " -> unset :component-root")
|
||||
(dissoc shape :component-root))]
|
||||
|
||||
@@ -404,7 +406,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a top main head.
|
||||
;; Convert the shape in a top main head.
|
||||
(log/debug :hint " -> set :component-root")
|
||||
(assoc shape :component-root true))]
|
||||
|
||||
@@ -418,7 +420,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a nested copy head.
|
||||
;; Convert the shape in a nested copy head.
|
||||
(log/debug :hint " -> unset :component-root")
|
||||
(dissoc shape :component-root))]
|
||||
|
||||
@@ -431,7 +433,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a top copy root.
|
||||
;; Convert the shape in a top copy root.
|
||||
(log/debug :hint " -> set :component-root")
|
||||
(assoc shape :component-root true))]
|
||||
|
||||
@@ -444,7 +446,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Detach the shape and convert it to non instance.
|
||||
;; Detach the shape and convert it to non instance.
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
|
||||
@@ -457,7 +459,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Detach the shape and convert it to non instance.
|
||||
;; Detach the shape and convert it to non instance.
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
|
||||
@@ -470,7 +472,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; There is no solution that may recover it with confidence
|
||||
;; There is no solution that may recover it with confidence
|
||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
shape)]
|
||||
|
||||
@@ -483,7 +485,7 @@
|
||||
[_ {:keys [shape page-id] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a frame.
|
||||
;; Convert the shape in a frame.
|
||||
(log/debug :hint " -> set :type :frame")
|
||||
(assoc shape :type :frame
|
||||
:fills []
|
||||
@@ -502,7 +504,7 @@
|
||||
[_ {:keys [shape] :as error} file-data _]
|
||||
(let [repair-component
|
||||
(fn [component]
|
||||
; Remove the objects key, or set it to {} if the component is deleted
|
||||
;; Remove the objects key, or set it to {} if the component is deleted
|
||||
(if (:deleted component)
|
||||
(do
|
||||
(log/debug :hint " -> set :objects {}")
|
||||
|
||||
@@ -430,8 +430,8 @@
|
||||
(assoc :frame-id frame-id)
|
||||
(assoc :svg-viewbox vbox)
|
||||
(assoc :svg-attrs props)
|
||||
;; We need to ensure fills are empty on import process
|
||||
;; because setup-shape assings one by default.
|
||||
;; We need to ensure fills are empty on import process
|
||||
;; because setup-shape assings one by default.
|
||||
(assoc :fills [])
|
||||
(merge radius-attrs)))))
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#{:audit-log
|
||||
:audit-log-archive
|
||||
:audit-log-gc
|
||||
:audit-log-logger
|
||||
:auto-file-snapshot
|
||||
;; enables the `/api/doc` endpoint that lists all the rpc methods available.
|
||||
:backend-api-doc
|
||||
@@ -147,7 +148,10 @@
|
||||
|
||||
;; A temporal flag, enables backend code use more extensivelly
|
||||
;; redis for caching data
|
||||
:redis-cache})
|
||||
:redis-cache
|
||||
|
||||
;; Activates the nitrate module
|
||||
:nitrate})
|
||||
|
||||
(def all-flags
|
||||
(set/union email login varia))
|
||||
|
||||
@@ -124,33 +124,51 @@
|
||||
|
||||
(defn adjust-to-viewport
|
||||
([viewport srect] (adjust-to-viewport viewport srect nil))
|
||||
([viewport srect {:keys [padding] :or {padding 0}}]
|
||||
([viewport srect {:keys [padding min-zoom] :or {padding 0 min-zoom nil}}]
|
||||
(let [gprop (/ (:width viewport)
|
||||
(:height viewport))
|
||||
srect (-> srect
|
||||
(update :x #(- % padding))
|
||||
(update :y #(- % padding))
|
||||
(update :width #(+ % padding padding))
|
||||
(update :height #(+ % padding padding)))
|
||||
width (:width srect)
|
||||
height (:height srect)
|
||||
lprop (/ width height)]
|
||||
(cond
|
||||
(> gprop lprop)
|
||||
(let [width' (* (/ width lprop) gprop)
|
||||
padding (/ (- width' width) 2)]
|
||||
(-> srect
|
||||
(update :x #(- % padding))
|
||||
(assoc :width width')
|
||||
(grc/update-rect :position)))
|
||||
srect-padded (-> srect
|
||||
(update :x #(- % padding))
|
||||
(update :y #(- % padding))
|
||||
(update :width #(+ % padding padding))
|
||||
(update :height #(+ % padding padding)))
|
||||
width (:width srect-padded)
|
||||
height (:height srect-padded)
|
||||
lprop (/ width height)
|
||||
adjusted-rect
|
||||
(cond
|
||||
(> gprop lprop)
|
||||
(let [width' (* (/ width lprop) gprop)
|
||||
padding (/ (- width' width) 2)]
|
||||
(-> srect-padded
|
||||
(update :x #(- % padding))
|
||||
(assoc :width width')
|
||||
(grc/update-rect :position)))
|
||||
|
||||
(< gprop lprop)
|
||||
(let [height' (/ (* height lprop) gprop)
|
||||
padding (/ (- height' height) 2)]
|
||||
(-> srect
|
||||
(update :y #(- % padding))
|
||||
(assoc :height height')
|
||||
(grc/update-rect :position)))
|
||||
(< gprop lprop)
|
||||
(let [height' (/ (* height lprop) gprop)
|
||||
padding (/ (- height' height) 2)]
|
||||
(-> srect-padded
|
||||
(update :y #(- % padding))
|
||||
(assoc :height height')
|
||||
(grc/update-rect :position)))
|
||||
|
||||
:else
|
||||
(grc/update-rect srect :position)))))
|
||||
:else
|
||||
(grc/update-rect srect-padded :position))]
|
||||
;; If min-zoom is specified and the resulting zoom would be below it,
|
||||
;; return a rect with the original top-left corner centered in the viewport
|
||||
;; instead of using the aspect-ratio-adjusted rect (which can push coords
|
||||
;; extremely far with extreme aspect ratios).
|
||||
(if (and (some? min-zoom)
|
||||
(< (/ (:width viewport) (:width adjusted-rect)) min-zoom))
|
||||
(let [anchor-x (:x srect)
|
||||
anchor-y (:y srect)
|
||||
vbox-width (/ (:width viewport) min-zoom)
|
||||
vbox-height (/ (:height viewport) min-zoom)]
|
||||
(-> adjusted-rect
|
||||
(assoc :x (- anchor-x (/ vbox-width 2))
|
||||
:y (- anchor-y (/ vbox-height 2))
|
||||
:width vbox-width
|
||||
:height vbox-height)
|
||||
(grc/update-rect :position)))
|
||||
adjusted-rect))))
|
||||
|
||||
@@ -75,20 +75,25 @@
|
||||
|
||||
#?(:cljs
|
||||
(defn ->clj
|
||||
[o & {:keys [key-fn val-fn] :or {key-fn read-kebab-key val-fn identity}}]
|
||||
[o & {:keys [key-fn val-fn recursive] :or {key-fn read-kebab-key val-fn identity recursive true}}]
|
||||
(let [f (fn this-fn [x]
|
||||
(let [x (val-fn x)]
|
||||
(cond
|
||||
(array? x)
|
||||
(persistent!
|
||||
(.reduce ^js/Array x
|
||||
#(conj! %1 (this-fn %2))
|
||||
#(conj! %1 (if recursive
|
||||
(this-fn %2)
|
||||
%2))
|
||||
(transient [])))
|
||||
|
||||
(identical? (type x) js/Object)
|
||||
(persistent!
|
||||
(.reduce ^js/Array (js-keys x)
|
||||
#(assoc! %1 (key-fn %2) (this-fn (unchecked-get x %2)))
|
||||
#(assoc! %1 (key-fn %2)
|
||||
(if recursive
|
||||
(this-fn (unchecked-get x %2))
|
||||
(unchecked-get x %2)))
|
||||
(transient {})))
|
||||
|
||||
:else
|
||||
|
||||
@@ -49,9 +49,9 @@
|
||||
(def log-container-ids #{})
|
||||
|
||||
(def updatable-attrs (->> (seq (keys ctk/sync-attrs))
|
||||
;; We don't update the flex-child attrs
|
||||
;; We don't update the flex-child attrs
|
||||
(remove ctk/swap-keep-attrs)
|
||||
;; We don't do automatic update of the `layout-grid-cells` property.
|
||||
;; We don't do automatic update of the `layout-grid-cells` property.
|
||||
(remove #(= :layout-grid-cells %))))
|
||||
|
||||
(defn enabled-shape?
|
||||
@@ -1898,10 +1898,10 @@
|
||||
(gsh/absolute-move shape new-pos)))
|
||||
|
||||
(defn- switch-path-change-value
|
||||
[prev-shape ;; The shape before the switch
|
||||
current-shape ;; The shape after the switch (a clean copy)
|
||||
ref-shape ;; The referenced shape on the main component
|
||||
;; before the switch
|
||||
[prev-shape ; The shape before the switch
|
||||
current-shape ; The shape after the switch (a clean copy)
|
||||
ref-shape ; The referenced shape on the main component
|
||||
; before the switch
|
||||
attr]
|
||||
(let [old-width (-> ref-shape :selrect :width)
|
||||
new-width (-> prev-shape :selrect :width)
|
||||
@@ -1918,10 +1918,9 @@
|
||||
|
||||
|
||||
(defn- switch-text-change-value
|
||||
[prev-content ;; The :content of the text before the switch
|
||||
current-content ;; The :content of the text after the switch (a clean copy)
|
||||
ref-content touched] ;; The :content of the referenced text on the main component
|
||||
;; before the switch
|
||||
[prev-content ; The :content of the text before the switch
|
||||
current-content ; The :content of the text after the switch (a clean copy)
|
||||
ref-content touched] ; The :content of the referenced text on the main component before the switch
|
||||
(let [;; We need the differences between the contents on the main
|
||||
;; components. current-content is the content of a clean copy,
|
||||
;; so for all effects its the same as the content on its main
|
||||
@@ -2497,7 +2496,7 @@
|
||||
(-> changes
|
||||
(cls/generate-delete-shapes
|
||||
file page objects (d/ordered-set (:id shape))
|
||||
{:allow-altering-copies true :ignore-children-fn ignore-swapped-fn :ignore-mask true}))
|
||||
{:allow-altering-copies true :ignore-children-fn ignore-swapped-fn :ignore-mask true :ignore-flows-for #{(:id shape)}}))
|
||||
[new-shape changes]
|
||||
(-> changes
|
||||
(generate-new-shape-for-swap shape file page libraries id-new-component index target-cell keep-props-values))]
|
||||
@@ -2845,8 +2844,8 @@
|
||||
duplicating-component?
|
||||
true
|
||||
(and remove-swap-slot?
|
||||
;; only remove swap slot of children when the current shape
|
||||
;; is not a subinstance head nor a instance root
|
||||
;; only remove swap slot of children when the current shape
|
||||
;; is not a subinstance head nor a instance root
|
||||
(not subinstance-head?)
|
||||
(not instance-root?))
|
||||
variant-props))
|
||||
@@ -2902,7 +2901,7 @@
|
||||
variant-props)
|
||||
changes))
|
||||
|
||||
;; We need to check the changes to get the ids-map
|
||||
;; We need to check the changes to get the ids-map
|
||||
ids-map
|
||||
(into {}
|
||||
(comp
|
||||
|
||||
@@ -124,9 +124,11 @@
|
||||
;; on the deletion process. It should receive a shape and
|
||||
;; return a boolean
|
||||
ignore-children-fn
|
||||
ignore-mask]
|
||||
ignore-mask
|
||||
ignore-flows-for]
|
||||
:or {ignore-children-fn (constantly false)
|
||||
ignore-mask false}}]
|
||||
ignore-mask false
|
||||
ignore-flows-for #{}}}]
|
||||
(let [objects (pcb/get-objects changes)
|
||||
data (pcb/get-library-data changes)
|
||||
page-id (pcb/get-page-id changes)
|
||||
@@ -136,12 +138,12 @@
|
||||
ids (cfh/clean-loops objects ids)
|
||||
in-component-copy?
|
||||
(fn [shape-id]
|
||||
;; Look for shapes that are inside a component copy, but are
|
||||
;; not the root. In this case, they must not be deleted,
|
||||
;; but hidden (to be able to recover them more easily).
|
||||
;; If we want to specifically allow altering the copies, this is
|
||||
;; a special case, like a component swap, in which case we want
|
||||
;; to delete the old shape
|
||||
;; Look for shapes that are inside a component copy, but are
|
||||
;; not the root. In this case, they must not be deleted,
|
||||
;; but hidden (to be able to recover them more easily).
|
||||
;; If we want to specifically allow altering the copies, this is
|
||||
;; a special case, like a component swap, in which case we want
|
||||
;; to delete the old shape
|
||||
(let [shape (get objects shape-id)]
|
||||
(and (ctn/has-any-copy-parent? objects shape)
|
||||
(not allow-altering-copies))))
|
||||
@@ -166,9 +168,9 @@
|
||||
groups-to-unmask
|
||||
(when-not ignore-mask
|
||||
(reduce (fn [group-ids id]
|
||||
;; When the shape to delete is the mask of a masked group,
|
||||
;; the mask condition must be removed, and it must be
|
||||
;; converted to a normal group.
|
||||
;; When the shape to delete is the mask of a masked group,
|
||||
;; the mask condition must be removed, and it must be
|
||||
;; converted to a normal group.
|
||||
(let [obj (lookup id)
|
||||
parent (lookup (:parent-id obj))]
|
||||
(if (and (:masked-group parent)
|
||||
@@ -181,8 +183,8 @@
|
||||
|
||||
interacting-shapes
|
||||
(filter (fn [shape]
|
||||
;; If any of the deleted shapes is the destination of
|
||||
;; some interaction, this must be deleted, too.
|
||||
;; If any of the deleted shapes is the destination of
|
||||
;; some interaction, this must be deleted, too.
|
||||
(let [interactions (:interactions shape)]
|
||||
(some #(and (ctsi/has-destination %)
|
||||
(contains? ids-to-delete (:destination %)))
|
||||
@@ -194,7 +196,8 @@
|
||||
(->> (:flows page)
|
||||
(reduce
|
||||
(fn [changes [id flow]]
|
||||
(if (id-to-delete? (:starting-frame flow))
|
||||
(if (and (id-to-delete? (:starting-frame flow))
|
||||
(not (contains? ignore-flows-for (:starting-frame flow))))
|
||||
(-> changes
|
||||
(pcb/with-page page)
|
||||
(pcb/set-flow id nil))
|
||||
@@ -204,7 +207,7 @@
|
||||
|
||||
all-parents
|
||||
(reduce (fn [res id]
|
||||
;; All parents of any deleted shape must be resized.
|
||||
;; All parents of any deleted shape must be resized.
|
||||
(into res (cfh/get-parent-ids objects id)))
|
||||
(d/ordered-set)
|
||||
(concat ids-to-delete ids-to-hide))
|
||||
@@ -236,10 +239,10 @@
|
||||
(recursive-find-empty-parents parents))))
|
||||
|
||||
empty-parents
|
||||
;; Any parent whose children are all deleted, must be deleted too.
|
||||
;; If we want to specifically allow altering the copies, this is a special case,
|
||||
;; for example during a component swap. in this case we are replacing a shape by
|
||||
;; other one, so must not delete empty parents.
|
||||
;; Any parent whose children are all deleted, must be deleted too.
|
||||
;; If we want to specifically allow altering the copies, this is a special case,
|
||||
;; for example during a component swap. in this case we are replacing a shape by
|
||||
;; other one, so must not delete empty parents.
|
||||
(if-not allow-altering-copies
|
||||
(into (d/ordered-set) (find-all-empty-parents #{}))
|
||||
#{})
|
||||
@@ -271,8 +274,8 @@
|
||||
guides-to-delete)
|
||||
|
||||
changes (reduce (fn [changes component-id]
|
||||
;; It's important to delete the component before the main instance, because we
|
||||
;; need to store the instance position if we want to restore it later.
|
||||
;; It's important to delete the component before the main instance, because we
|
||||
;; need to store the instance position if we want to restore it later.
|
||||
(pcb/delete-component changes component-id (:id page)))
|
||||
changes
|
||||
components-to-delete)
|
||||
@@ -324,7 +327,7 @@
|
||||
result #{}]
|
||||
|
||||
(if-not current-id
|
||||
;; Base case, no next element
|
||||
;; Base case, no next element
|
||||
result
|
||||
|
||||
(let [group (get objects current-id)]
|
||||
@@ -332,14 +335,14 @@
|
||||
(not= current-id parent-id)
|
||||
(empty? (remove removed-id? (:shapes group))))
|
||||
|
||||
;; Adds group to the remove and check its parent
|
||||
;; Adds group to the remove and check its parent
|
||||
(let [to-check (concat to-check [(cfh/get-parent-id objects current-id)])]
|
||||
(recur (first to-check)
|
||||
(rest to-check)
|
||||
(conj removed-id? current-id)
|
||||
(conj result current-id)))
|
||||
|
||||
;; otherwise recur
|
||||
;; otherwise recur
|
||||
(recur (first to-check)
|
||||
(rest to-check)
|
||||
removed-id?
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
"Check if any ancestor of a shape (between base-parent-id and shape) was swapped"
|
||||
[shape objects base-parent-id]
|
||||
(let [ancestors (->> (ctn/get-parent-heads objects shape)
|
||||
;; Ignore ancestors ahead of base-parent
|
||||
;; Ignore ancestors ahead of base-parent
|
||||
(drop-while #(not= base-parent-id (:id %)))
|
||||
seq)
|
||||
num-ancestors (count ancestors)
|
||||
|
||||
@@ -132,3 +132,94 @@ Some naming conventions:
|
||||
(if-let [last-period (str/last-index-of s ".")]
|
||||
[(subs s 0 (inc last-period)) (subs s (inc last-period))]
|
||||
[s ""]))
|
||||
|
||||
;; Tree building functions --------------------------------------------------
|
||||
|
||||
"Build tree structure from flat list of paths"
|
||||
|
||||
"`build-tree-root` is the main function to build the tree."
|
||||
|
||||
"Receives a list of segments with 'name' properties representing paths,
|
||||
and a separator string."
|
||||
"E.g segments = [{... :name 'one/two/three'} {... :name 'one/two/four'} {... :name 'one/five'}]"
|
||||
|
||||
"Transforms into a tree structure like:
|
||||
[{:name 'one'
|
||||
:path 'one'
|
||||
:depth 0
|
||||
:leaf nil
|
||||
:children-fn (fn [] [{:name 'two'
|
||||
:path 'one.two'
|
||||
:depth 1
|
||||
:leaf nil
|
||||
:children-fn (fn [] [{... :name 'three'} {... :name 'four'}])}
|
||||
{:name 'five'
|
||||
:path 'one.five'
|
||||
:depth 1
|
||||
:leaf {... :name 'five'}
|
||||
...}])}]"
|
||||
|
||||
(defn- sort-by-children
|
||||
"Sorts segments so that those with children come first."
|
||||
[segments separator]
|
||||
(sort-by (fn [segment]
|
||||
(let [path (split-path (:name segment) :separator separator)
|
||||
path-length (count path)]
|
||||
(if (= path-length 1)
|
||||
1
|
||||
0)))
|
||||
segments))
|
||||
|
||||
(defn- group-by-first-segment
|
||||
"Groups segments by their first path segment and update segment name."
|
||||
[segments separator]
|
||||
(reduce (fn [acc segment]
|
||||
(let [[first-segment & remaining-segments] (split-path (:name segment) :separator separator)
|
||||
rest-path (when (seq remaining-segments) (join-path remaining-segments :separator separator :with-spaces? false))]
|
||||
(update acc first-segment (fnil conj [])
|
||||
(if rest-path
|
||||
(assoc segment :name rest-path)
|
||||
segment))))
|
||||
{}
|
||||
segments))
|
||||
|
||||
(defn- sort-and-group-segments
|
||||
"Sorts elements and groups them by their first path segment."
|
||||
[segments separator]
|
||||
(let [sorted (sort-by-children segments separator)
|
||||
grouped (group-by-first-segment sorted separator)]
|
||||
grouped))
|
||||
|
||||
(defn- build-tree-node
|
||||
"Builds a single tree node with lazy children."
|
||||
[segment-name remaining-segments separator parent-path depth]
|
||||
(let [current-path (if parent-path
|
||||
(str parent-path "." segment-name)
|
||||
segment-name)
|
||||
|
||||
is-leaf? (and (seq remaining-segments)
|
||||
(every? (fn [segment]
|
||||
(let [remaining-segment-name (first (split-path (:name segment) :separator separator))]
|
||||
(= segment-name remaining-segment-name)))
|
||||
remaining-segments))
|
||||
|
||||
leaf-segment (when is-leaf? (first remaining-segments))
|
||||
node {:name segment-name
|
||||
:path current-path
|
||||
:depth depth
|
||||
:leaf leaf-segment
|
||||
:children-fn (when-not is-leaf?
|
||||
(fn []
|
||||
(let [grouped-elements (sort-and-group-segments remaining-segments separator)]
|
||||
(mapv (fn [[child-segment-name remaining-child-segments]]
|
||||
(build-tree-node child-segment-name remaining-child-segments separator current-path (inc depth)))
|
||||
grouped-elements))))}]
|
||||
node))
|
||||
|
||||
(defn build-tree-root
|
||||
"Builds the root level of the tree."
|
||||
[segments separator]
|
||||
(let [grouped-elements (sort-and-group-segments segments separator)]
|
||||
(mapv (fn [[segment-name remaining-segments]]
|
||||
(build-tree-node segment-name remaining-segments separator nil 0))
|
||||
grouped-elements)))
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
:else
|
||||
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
||||
|
||||
;; We add an end-of-line when finish a paragraph
|
||||
;; We add an end-of-line when finish a paragraph
|
||||
new-acc
|
||||
(if (= (:type node) "paragraph")
|
||||
(let [[hs ht] (first new-acc)]
|
||||
|
||||
@@ -64,8 +64,33 @@
|
||||
java.time.temporal.TemporalAmount
|
||||
java.time.temporal.TemporalUnit)))
|
||||
|
||||
(declare inst)
|
||||
|
||||
#?(:clj (def ^:dynamic *clock* (Clock/systemDefaultZone)))
|
||||
|
||||
#?(:clj
|
||||
(defn clock?
|
||||
[o]
|
||||
(instance? Clock o)))
|
||||
|
||||
#?(:clj
|
||||
(defn get-system-clock
|
||||
[]
|
||||
(Clock/systemDefaultZone)))
|
||||
|
||||
#?(:clj
|
||||
(defn offset-clock
|
||||
[offset]
|
||||
(Clock/offset ^Clock (Clock/systemDefaultZone) ^Duration offset)))
|
||||
|
||||
#?(:clj
|
||||
(defn fixed-clock
|
||||
[instant]
|
||||
(Clock/fixed ^Instant (inst instant)
|
||||
^ZoneId (ZoneId/of "Z"))))
|
||||
|
||||
|
||||
|
||||
(defn now
|
||||
[]
|
||||
#?(:clj (Instant/now *clock*)
|
||||
|
||||
@@ -140,7 +140,8 @@
|
||||
:layout-item-min-w
|
||||
:layout-item-absolute
|
||||
:layout-item-z-index
|
||||
:layout-item-align-self})
|
||||
:layout-item-align-self
|
||||
:interactions})
|
||||
|
||||
(defn component-attr?
|
||||
"Check if some attribute is one that is involved in component syncrhonization.
|
||||
|
||||
@@ -458,13 +458,13 @@
|
||||
(map #(cfh/components-nesting-loop? objects (:id %) (:id parent)))
|
||||
(every? nil?)))]
|
||||
(or
|
||||
;;We don't want to change the structure of component copies
|
||||
;;We don't want to change the structure of component copies
|
||||
(ctk/in-component-copy? parent)
|
||||
(has-any-copy-parent? objects parent)
|
||||
;; If we are moving something containing a main instance the container can't be part of a component (neither main nor copy)
|
||||
;; If we are moving something containing a main instance the container can't be part of a component (neither main nor copy)
|
||||
(and selected-main-instance? parent-in-component?)
|
||||
;; Avoid placing a shape as a direct or indirect child of itself,
|
||||
;; or inside its main component if it's in a copy.
|
||||
;; Avoid placing a shape as a direct or indirect child of itself,
|
||||
;; or inside its main component if it's in a copy.
|
||||
comps-nesting-loop?)))
|
||||
|
||||
(defn find-valid-parent-and-frame-ids
|
||||
|
||||
@@ -775,9 +775,9 @@
|
||||
file-data (cond-> file-data
|
||||
(d/not-empty? used-components)
|
||||
(absorb-components used-components library-data))
|
||||
;; Note that absorbed components may also be using colors
|
||||
;; and typographies. This is the reason of doing this first
|
||||
;; and accumulating file data for the next ones.
|
||||
;; Note that absorbed components may also be using colors
|
||||
;; and typographies. This is the reason of doing this first
|
||||
;; and accumulating file data for the next ones.
|
||||
|
||||
used-colors (find-asset-type-usages file-data library-data :color)
|
||||
file-data (cond-> file-data
|
||||
@@ -1017,7 +1017,7 @@
|
||||
libs-to-show
|
||||
(-> libs-to-show
|
||||
(add-component library-id component-id))))))
|
||||
;; (find-used-components-cumulative page root)
|
||||
;; (find-used-components-cumulative page root)
|
||||
|
||||
libs-to-show
|
||||
components))
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
|
||||
(-write-to [_ heap offset]
|
||||
(let [buffer' (.-buffer ^js/DataView dbuffer)
|
||||
;; Calculate byte size: 4 bytes header + (size * FILL-U8-SIZE)
|
||||
;; Calculate byte size: 4 bytes header + (size * FILL-U8-SIZE)
|
||||
byte-size (+ 4 (* size FILL-U8-SIZE))
|
||||
;; Create Uint32Array with exact size needed (convert bytes to u32 elements)
|
||||
u32-array (js/Uint32Array. buffer' 0 (/ byte-size 4))]
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.schema.generators :as sg]))
|
||||
|
||||
;; WARNING: options are not deleted when changing event or action type, so it can be
|
||||
;; restored if the user changes it back later.
|
||||
;; WARNING: options are not deleted when changing event or action
|
||||
;; type, so it can be restored if the user changes it back later.
|
||||
;;
|
||||
;; But that means that an interaction may have for example a delay or
|
||||
;; destination, even if its type does not require it (but a previous type did).
|
||||
;; But that means that an interaction may have for example a delay or
|
||||
;; destination, even if its type does not require it (but a previous
|
||||
;; type did).
|
||||
;;
|
||||
;; So make sure to use has-delay/has-destination... functions, or similar,
|
||||
;; before reading them.
|
||||
;; So make sure to use has-delay/has-destination... functions, or
|
||||
;; similar, before reading them.
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMA
|
||||
@@ -452,16 +453,15 @@
|
||||
(gpt/point 0 0)))
|
||||
|
||||
(defn calc-overlay-position
|
||||
[interaction ;; interaction data
|
||||
shape ;; Shape with the interaction
|
||||
objects ;; the objects tree
|
||||
relative-to-shape ;; the interaction position is realtive to this
|
||||
;; sape
|
||||
base-frame ;; the base frame of the current interaction
|
||||
dest-frame ;; the frame to display with this interaction
|
||||
frame-offset] ;; if this interaction starts in a frame opened
|
||||
;; on another interaction, this is the position
|
||||
;; of that frame
|
||||
[interaction ; interaction data
|
||||
shape ; Shape with the interaction
|
||||
objects ; the objects tree
|
||||
relative-to-shape ; the interaction position is realtive to this shape
|
||||
base-frame ; the base frame of the current interaction
|
||||
dest-frame ; the frame to display with this interaction
|
||||
frame-offset] ; if this interaction starts in a frame opened
|
||||
; on another interaction, this is the position
|
||||
; of that frame
|
||||
(assert (check-interaction interaction))
|
||||
(assert (has-overlay-opts interaction)
|
||||
"expected compatible interaction map")
|
||||
|
||||
@@ -382,9 +382,9 @@
|
||||
keep-ids? (:id shape)
|
||||
:else (uuid/next))
|
||||
|
||||
;; Assign the correct frame-id for the given parent. It's the parent-id (if parent is frame)
|
||||
;; or the parent's frame-id otherwise. Only for the first cloned shapes. In recursive calls
|
||||
;; this is not needed.
|
||||
;; Assign the correct frame-id for the given parent. It's the parent-id (if parent is frame)
|
||||
;; or the parent's frame-id otherwise. Only for the first cloned shapes. In recursive calls
|
||||
;; this is not needed.
|
||||
frame-id (cond
|
||||
(and (nil? frame-id) (cfh/frame-shape? dest-objects parent-id))
|
||||
parent-id
|
||||
|
||||
@@ -393,7 +393,7 @@
|
||||
:else
|
||||
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
||||
|
||||
;; We add an end-of-line when finish a paragraph
|
||||
;; We add an end-of-line when finish a paragraph
|
||||
new-acc
|
||||
(if (= (:type node) "paragraph")
|
||||
(let [[hs ht] (first new-acc)]
|
||||
|
||||
@@ -47,6 +47,18 @@
|
||||
self-reference? (get token-references token-name)]
|
||||
self-reference?))
|
||||
|
||||
(defn references-token?
|
||||
"Recursively check if a value references the token name. Handles strings, maps, and sequences."
|
||||
[value token-name]
|
||||
(cond
|
||||
(string? value)
|
||||
(boolean (some #(= % token-name) (find-token-value-references value)))
|
||||
(map? value)
|
||||
(some true? (map #(references-token? % token-name) (vals value)))
|
||||
(sequential? value)
|
||||
(some true? (map #(references-token? % token-name) value))
|
||||
:else false))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMA
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -99,7 +111,7 @@
|
||||
|
||||
(def token-name-ref
|
||||
[:re {:title "TokenNameRef" :gen/gen sg/text}
|
||||
#"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"])
|
||||
#"^[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*$"])
|
||||
|
||||
(def ^:private schema:color
|
||||
[:map
|
||||
@@ -462,8 +474,8 @@
|
||||
:height #{:sizing :dimensions}
|
||||
:max-width #{:sizing :dimensions}
|
||||
:max-height #{:sizing :dimensions}
|
||||
:x #{:spacing :dimensions}
|
||||
:y #{:spacing :dimensions}
|
||||
:x #{:dimensions}
|
||||
:y #{:dimensions}
|
||||
:rotation #{:number :rotation}
|
||||
:border-radius #{:border-radius :dimensions}
|
||||
:row-gap #{:spacing :dimensions}
|
||||
@@ -475,6 +487,8 @@
|
||||
:vertical-margin #{:spacing :dimensions}
|
||||
:sided-margins #{:spacing :dimensions}
|
||||
:line-height #{:line-height :number}
|
||||
:opacity #{:opacity}
|
||||
:stroke-width #{:stroke-width :dimensions}
|
||||
:font-size #{:font-size}
|
||||
:letter-spacing #{:letter-spacing}
|
||||
:fill #{:color}
|
||||
@@ -558,3 +572,18 @@
|
||||
"Predicate if a shadow composite token is a reference value - a string pointing to another reference token."
|
||||
[token-value]
|
||||
(string? token-value))
|
||||
|
||||
(defn update-token-value-references
|
||||
"Recursively update token references within a token value, supporting complex token values (maps, sequences, strings)."
|
||||
[value old-name new-name]
|
||||
(cond
|
||||
(string? value)
|
||||
(str/replace value
|
||||
(re-pattern (str "\\{" (str/replace old-name "." "\\.") "\\}"))
|
||||
(str "{" new-name "}"))
|
||||
(map? value)
|
||||
(d/update-vals value #(update-token-value-references % old-name new-name))
|
||||
(sequential? value)
|
||||
(mapv #(update-token-value-references % old-name new-name) value)
|
||||
:else
|
||||
value))
|
||||
|
||||
@@ -909,7 +909,8 @@ Will return a value that matches this schema:
|
||||
`:all` All of the nested sets are active
|
||||
`:partial` Mixed active state of nested sets")
|
||||
(get-tokens-in-active-sets [_] "set of set names that are active in the the active themes")
|
||||
(get-all-tokens [_] "all tokens in the lib")
|
||||
(get-all-tokens [_] "all tokens in the lib, as a sequence")
|
||||
(get-all-tokens-map [_] "all tokens in the lib, as a map name -> token")
|
||||
(get-tokens [_ set-id] "return a map of tokens in the set, indexed by token-name"))
|
||||
|
||||
(declare parse-multi-set-dtcg-json)
|
||||
@@ -1306,6 +1307,10 @@ Will return a value that matches this schema:
|
||||
tokens))
|
||||
|
||||
(get-all-tokens [this]
|
||||
(mapcat #(vals (get-tokens- %))
|
||||
(get-sets this)))
|
||||
|
||||
(get-all-tokens-map [this]
|
||||
(reduce
|
||||
(fn [tokens' set]
|
||||
(into tokens' (map (fn [x] [(:name x) x]) (vals (get-tokens- set)))))
|
||||
|
||||
@@ -693,7 +693,7 @@
|
||||
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||
#{(:id main-child)}
|
||||
(fn [shape]
|
||||
;; Update the attrs on all the content tree
|
||||
;; Update the attrs on all the content tree
|
||||
(-> shape
|
||||
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
||||
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
||||
@@ -851,7 +851,7 @@
|
||||
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||
#{(:id main-child)}
|
||||
(fn [shape]
|
||||
;; Update the attrs on all the content tree
|
||||
;; Update the attrs on all the content tree
|
||||
(-> shape
|
||||
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
||||
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
||||
|
||||
@@ -267,7 +267,7 @@
|
||||
page' (thf/current-page file')
|
||||
objects' (:objects page')]
|
||||
|
||||
;; ==== Check
|
||||
;; ==== Check
|
||||
(thf/validate-file! file')
|
||||
(t/is (= (count (:components data)) 2))
|
||||
(t/is (= (count (:components data')) 4))
|
||||
|
||||
1291
common/yarn.lock
1291
common/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -179,9 +179,9 @@ RUN set -eux; \
|
||||
|
||||
FROM base AS setup-utils
|
||||
|
||||
ENV CLJKONDO_VERSION=2025.07.28 \
|
||||
ENV CLJKONDO_VERSION=2026.01.19 \
|
||||
BABASHKA_VERSION=1.12.208 \
|
||||
CLJFMT_VERSION=0.13.1
|
||||
CLJFMT_VERSION=0.15.6
|
||||
|
||||
RUN set -ex; \
|
||||
ARCH="$(dpkg --print-architecture)"; \
|
||||
@@ -398,7 +398,6 @@ COPY files/Caddyfile /home/
|
||||
COPY files/selfsigned.crt /home/
|
||||
COPY files/selfsigned.key /home/
|
||||
COPY files/start-tmux.sh /home/start-tmux.sh
|
||||
COPY files/start-tmux-back.sh /home/start-tmux-back.sh
|
||||
COPY files/entrypoint.sh /home/entrypoint.sh
|
||||
COPY files/init.sh /home/init.sh
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
{
|
||||
auto_https off
|
||||
auto_https off
|
||||
}
|
||||
|
||||
localhost:3449 {
|
||||
reverse_proxy localhost:4449
|
||||
tls /home/selfsigned.crt /home/selfsigned.key
|
||||
reverse_proxy localhost:4449
|
||||
tls /home/selfsigned.crt /home/selfsigned.key
|
||||
header -Strict-Transport-Security
|
||||
}
|
||||
|
||||
http://localhost:3450 {
|
||||
reverse_proxy localhost:4449
|
||||
reverse_proxy localhost:4449
|
||||
}
|
||||
|
||||
http://penpot-devenv-main:3450 {
|
||||
reverse_proxy localhost:4449
|
||||
reverse_proxy localhost:4449
|
||||
}
|
||||
|
||||
@@ -141,8 +141,14 @@ http {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
}
|
||||
|
||||
location /nitrate/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
location /control-center {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /wasm-playground {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sudo chown penpot:users /home/penpot
|
||||
|
||||
cd ~;
|
||||
|
||||
source ~/.bashrc
|
||||
|
||||
set -e;
|
||||
|
||||
echo "[start-tmux.sh] Installing node dependencies"
|
||||
pushd ~/penpot/exporter/
|
||||
yarn install
|
||||
popd
|
||||
|
||||
tmux -2 new-session -d -s penpot
|
||||
|
||||
tmux rename-window -t penpot:0 'exporter'
|
||||
tmux select-window -t penpot:0
|
||||
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
|
||||
tmux send-keys -t penpot 'rm -f target/app.js*' enter C-l
|
||||
tmux send-keys -t penpot 'clojure -M:dev:shadow-cljs watch main' enter
|
||||
|
||||
tmux split-window -v
|
||||
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
|
||||
tmux send-keys -t penpot './scripts/wait-and-start.sh' enter
|
||||
|
||||
tmux new-window -t penpot:1 -n 'backend'
|
||||
tmux select-window -t penpot:1
|
||||
tmux send-keys -t penpot 'cd penpot/backend' enter C-l
|
||||
tmux send-keys -t penpot './scripts/start-dev' enter
|
||||
|
||||
tmux -2 attach-session -t penpot
|
||||
@@ -112,10 +112,6 @@ COPY --from=penpotapp/imagemagick:7.1.2-0 /opt/imagick /opt/imagick
|
||||
WORKDIR /opt/penpot/exporter
|
||||
USER penpot:penpot
|
||||
|
||||
RUN set -ex; \
|
||||
corepack install; \
|
||||
yarn install; \
|
||||
yarn run playwright install chromium; \
|
||||
rm -rf /opt/penpot/.yarn
|
||||
RUN ./setup
|
||||
|
||||
CMD ["node", "app.js"]
|
||||
|
||||
@@ -29,8 +29,9 @@ update_flags /var/www/app/js/config.js
|
||||
|
||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
||||
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000}
|
||||
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
|
||||
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
||||
|
||||
@@ -139,12 +139,20 @@ http {
|
||||
proxy_pass $PENPOT_BACKEND_URI/ws/notifications;
|
||||
}
|
||||
|
||||
location /control-center {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $http_cf_connecting_ip;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_pass $PENPOT_NITRATE_URI$request_uri;
|
||||
}
|
||||
|
||||
include /etc/nginx/overrides/server.d/*.conf;
|
||||
|
||||
location / {
|
||||
include /etc/nginx/overrides/location.d/*.conf;
|
||||
|
||||
location ~* \.(js|css|jpg|png|svg|gif|ttf|woff|woff2|wasm)$ {
|
||||
location ~* \.(js|css|jpg|png|svg|gif|ttf|woff|woff2|wasm|map)$ {
|
||||
add_header Cache-Control "public, max-age=604800" always; # 7 days
|
||||
}
|
||||
|
||||
@@ -152,8 +160,10 @@ http {
|
||||
return 301 " /404";
|
||||
}
|
||||
|
||||
add_header X-Frame-Options SAMEORIGIN always;
|
||||
add_header Cache-Control "no-store, no-cache, max-age=0" always;
|
||||
try_files $uri /index.html$is_args$args /index.html =404;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,15 @@ To view this site locally, first set up the environment:
|
||||
# only if necessary
|
||||
nvm install
|
||||
nvm use
|
||||
# only if necessary
|
||||
corepack enable
|
||||
|
||||
yarn install
|
||||
pnpm install
|
||||
```
|
||||
|
||||
And launch a development server:
|
||||
|
||||
```sh
|
||||
yarn start
|
||||
pnpm start
|
||||
```
|
||||
|
||||
You can then point a browser to [http://localhost:8080](http://localhost:8080).
|
||||
|
||||
@@ -39,5 +39,5 @@
|
||||
"markdown-it-anchor": "^9.0.1",
|
||||
"markdown-it-plantuml": "^1.4.1"
|
||||
},
|
||||
"packageManager": "yarn@4.3.1"
|
||||
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ desc: Create, deploy, and use the Penpot plugin API with our comprehensive docum
|
||||
|
||||
# Penpot plugins API
|
||||
|
||||
We've got all the documentation you need for the API right <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/">here</a>.
|
||||
We've got all the documentation you need for the API right <a target="_blank" href="https://doc.plugins.penpot.app/">here</a>.
|
||||
|
||||
@@ -9,13 +9,13 @@ desc: See the Penpot plugin API changelog for version 1.0! Find breaking changes
|
||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"></g-emoji> Epics and highlights</code>
|
||||
- This marks the release of version 1.0, and from this point forward, we’ll do our best to avoid making any more breaking changes (or make deprecations backward compatible).
|
||||
- We’ve redone the documentation. You can check the API here:
|
||||
[https://penpot-plugins-api-doc.pages.dev/](https://penpot-plugins-api-doc.pages.dev/)
|
||||
[https://doc.plugins.penpot.app/](https://doc.plugins.penpot.app/)
|
||||
- New samples repository with lots of samples to use the API:
|
||||
[https://github.com/penpot/penpot-plugins-samples](https://github.com/penpot/penpot-plugins-samples)
|
||||
|
||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"></g-emoji> Breaking changes & Deprecations
|
||||
|
||||
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://penpot-plugins-api-doc.pages.dev/) for more details.
|
||||
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://doc.plugins.penpot.app/) for more details.
|
||||
- Changes on the <code class="language-js">penpot.on</code> and <code class="language-js">penpot.off</code> methods.
|
||||
Previously you had to send the original callback to the off method in order to remove an event listener. Now, <code class="language-js">penpot.on</code> will return an *id* that you can pass to the <code class="language-js">penpot.off</code> method in order to remove the listener.
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ There are two libraries that can help you with your plugin's development. They a
|
||||
|
||||
### Plugin styles
|
||||
|
||||
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">Plugin styles</a>.
|
||||
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://styles-doc.plugins.penpot.app/">Plugin styles</a>.
|
||||
|
||||
```bash
|
||||
npm install @penpot/plugin-styles
|
||||
@@ -139,7 +139,7 @@ parent.postMessage(responseMessage, targetOrigin);
|
||||
|
||||
By using these message-based events, any data retrieved through the Penpot API can be communicated to and from your plugin interface seamlessly.
|
||||
|
||||
For more detailed information, refer to the [Penpot Plugins API Documentation](https://penpot-plugins-api-doc.pages.dev/).
|
||||
For more detailed information, refer to the [Penpot Plugins API Documentation](https://doc.plugins.penpot.app/).
|
||||
|
||||
## 2.5. Step 5. Build the plugin file
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ penpot.library.local.createTypography();
|
||||
|
||||
Penpot has dark and light modes, and you can easily add this to your plugin so your interface adapts to both themes. When you add theme support, your plugin will automatically sync with Penpot's interface settings, so the user experience is consistent no matter which mode is selected. This makes your plugin look better and also ensures it stays in line with Penpot's overall design.
|
||||
|
||||
Just a heads-up: if you use the <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
||||
Just a heads-up: if you use the <a target="_blank" href="https://styles-doc.plugins.penpot.app/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
||||
|
||||
<a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/theme">Theme example</a>
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ The plugin <a target="_blank" href="https://www.npmjs.com/package/@penpot/plugin
|
||||
|
||||
### Is the API ready to use the prototyping features?
|
||||
|
||||
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotFlow">PenpotFlow</a> or <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotInteraction">PenpotInteraction</a> interfaces.
|
||||
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Flow">Flow</a> or <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Interaction">Interaction</a> interfaces.
|
||||
|
||||
### Are there any security or quality criteria I should be aware of?
|
||||
|
||||
@@ -48,7 +48,8 @@ There are no set requirements. However, we can recommend the use of <a target="_
|
||||
|
||||
### Is it necessary to create plugins with a UI?
|
||||
|
||||
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette-penpot-plugin.pages.dev/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot-plugins/tree/main/apps/create-palette-plugin">here</a>
|
||||
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette.plugins.penpot.app/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot/tree/main/plugins/apps/create-palette-plugin">here</a>
|
||||
|
||||
|
||||
### Can I create components?
|
||||
|
||||
@@ -58,7 +59,7 @@ Yes, it is possible to create components using:
|
||||
createComponent(shapes: Shape[]): LibraryComponent;
|
||||
```
|
||||
|
||||
Take a look at the Penpot Library methods in the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
||||
Take a look at the Penpot Library methods in the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
||||
|
||||
### Is there a place where I can share my plugin?
|
||||
|
||||
|
||||
@@ -69,12 +69,13 @@ You need to provide the plugin's manifest URL for the installation. If there are
|
||||
|
||||
| Name | URL |
|
||||
| ------------- | ------------------------------------------------------------------- |
|
||||
| Lorem Ipsum | https://lorem-ipsum-penpot-plugin.pages.dev/assets/manifest.json |
|
||||
| Contrast | https://contrast-penpot-plugin.pages.dev/assets/manifest.json |
|
||||
| Feather icons | https://icons-penpot-plugin.pages.dev/assets/manifest.json |
|
||||
| Tables | https://table-penpot-plugin.pages.dev/assets/manifest.json |
|
||||
| Color palette | https://create-palette-penpot-plugin.pages.dev/assets/manifest.json |
|
||||
| Rename layers | https://rename-layers-penpot-plugin.pages.dev/assets/manifest.json |
|
||||
| Color palette | https://create-palette.plugins.penpot.app/assets/manifest.json |
|
||||
| Contrast | https://contrast.plugins.penpot.app/assets/manifest.json |
|
||||
| Feather icons | https://icons.plugins.penpot.app/assets/manifest.json |
|
||||
| Lorem ipsum | https://lorem-ipsum.plugins.penpot.app/assets/manifest.json |
|
||||
| Rename layers | https://rename-layers.plugins.penpot.app/assets/manifest.json |
|
||||
| Tables | https://table.plugins.penpot.app/assets/manifest.json |
|
||||
|
||||
|
||||
## 1.4. Plugin's basics
|
||||
|
||||
|
||||
2065
docs/pnpm-lock.yaml
generated
Normal file
2065
docs/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,5 +6,5 @@ corepack enable;
|
||||
corepack install;
|
||||
|
||||
rm -rf ./_dist
|
||||
yarn install
|
||||
yarn run build
|
||||
pnpm install
|
||||
pnpm run build
|
||||
|
||||
@@ -281,8 +281,8 @@ for how to define custom metadata and other ways of selecting tests.
|
||||
it, but for now we use shadow-cljs with <code class="language-text">package.json</code> scripts:
|
||||
|
||||
```bash
|
||||
yarn run test
|
||||
yarn run test:watch
|
||||
pnpm run test
|
||||
pnpm run test:watch
|
||||
```
|
||||
|
||||
#### Test output
|
||||
|
||||
@@ -217,7 +217,7 @@ repository:
|
||||
|
||||
```bash
|
||||
# cd <repo>/frontend
|
||||
yarn run translations
|
||||
pnpm run translations
|
||||
```
|
||||
|
||||
At Penpot core team we maintain manually the english and spanish .po files. All
|
||||
@@ -308,7 +308,7 @@ Ensure your development environment docker image is up to date.
|
||||
This is not required, but it may be convenient to compile Penpot in release mode before running the tests. This way they will be much quicker and stable. For this, go to the frontend window in the tmux session (<code class="language-bash">Ctrl + b 1</code>), interrupt the watch process with <code class="language-bash">Ctrl + C</code> and type:
|
||||
|
||||
```bash
|
||||
yarn run build:app
|
||||
./scripts/build
|
||||
```
|
||||
|
||||
Obviously, in this mode if you make changes to the source code, you will need to repeat the build manually each time. It may be useful to use wath mode when debugging a single test, and use release mode to run all the suite.
|
||||
@@ -328,17 +328,17 @@ Here's how to run the tests with a headless browser (i.e. within the terminal, n
|
||||
cd penpot/frontend
|
||||
```
|
||||
|
||||
3. Run the tests with <code class="language-bash">yarn</code>:
|
||||
3. Run the tests with <code class="language-bash">pnpm</code>:
|
||||
|
||||
```bash
|
||||
yarn test:e2e
|
||||
pnpm run test:e2e
|
||||
```
|
||||
|
||||
> 💡 **TIP:** By default, the tests will _not_ run in parallel. You can set the amount of workers to run the tests with <code class="language-bash">--workers</code>. Note that, depending on your machine, this might make some tests flaky.
|
||||
|
||||
```bash
|
||||
# run in parallel with 4 workers
|
||||
yarn test:e2e --workers 4
|
||||
pnpm run test:e2e --workers 4
|
||||
```
|
||||
|
||||
#### Running the tests in Chromium
|
||||
@@ -356,7 +356,7 @@ npx playwright test --ui
|
||||
|
||||
> ❗️ **IMPORTANT**: You might need to [install Playwright's browsers and dependencies](https://playwright.dev/docs/intro) in your host machine with: <code class="language-bash">npx playwright install --with-deps</code>. In case you are using a Linux distribution other than Ubuntu, [you might need to install the dependencies manually](https://github.com/microsoft/playwright/issues/11122).
|
||||
|
||||
> You will also need yarn in your host nodejs. For this, do <code class="language-bash">corepack enable</code> and then just <code class="language-bash">yarn</code>.
|
||||
> You will also need pnpm in your host nodejs. For this, do <code class="language-bash">corepack enable</code> and then just <code class="language-bash">pnpm</code>.
|
||||
|
||||
### How to write a test
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user