From 0943411e47d450f52f859a0f9e55442f06d54f5a Mon Sep 17 00:00:00 2001 From: Erik Goins Date: Fri, 16 Oct 2026 09:51:14 -0300 Subject: [PATCH] =?UTF-8?q?docs:=20record=20owner=20decisions=20=E2=80=94?= =?UTF-8?q?=20Workers=20Paid=20plan=20(022),=20keep=20links,=20auto-detect?= =?UTF-8?q?=20mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GnDXTQ6MVbAXETYsq6fvqg --- apps/web/.gitignore 2 | 41 + apps/web/AGENTS 2.md | 9 + apps/web/README 2.md | 36 + apps/web/eslint.config 2.mjs | 22 + apps/web/next.config 2.ts | 18 + apps/web/package 2.json | 31 + apps/web/public/file 2.svg | 1 + apps/web/public/globe 2.svg | 1 + apps/web/public/next 2.svg | 1 + apps/web/public/vercel 2.svg | 1 + apps/web/public/window 2.svg | 1 + apps/web/src/app/favicon 2.ico | Bin 0 -> 15086 bytes apps/web/src/app/globals 2.css | 26 + apps/web/src/app/layout 2.tsx | 30 + apps/web/src/app/page 2.tsx | 10 + apps/web/tsconfig 2.json | 34 + apps/web/worker 2.ts | 25 + docs/INDEX.md | 1 + docs/STATE.md | 12 +- docs/decisions/009-tasks-projects-v2.md | 2 + docs/decisions/017-identity 2.md | 34 + docs/decisions/020-workspace-link-boundary.md | 4 +- docs/decisions/021-task-writes.md | 2 +- docs/decisions/022-cloudflare-paid-plan.md | 22 + docs/features/tasks.md | 6 +- docs/spec.md | 1 + eslint.config 2.mjs | 8 + package 2.json | 16 + .../db/migrations/0003_resumable_sweep 2.sql | 2 + .../db/migrations/meta/0003_snapshot 2.json | 1777 +++++++++++++++++ pnpm-lock 2.yaml | 167 ++ pnpm-workspace 2.yaml | 9 + 32 files changed, 2336 insertions(+), 14 deletions(-) create mode 100644 apps/web/.gitignore 2 create mode 100644 apps/web/AGENTS 2.md create mode 100644 apps/web/README 2.md create mode 100644 apps/web/eslint.config 2.mjs create mode 100644 apps/web/next.config 2.ts create mode 100644 apps/web/package 2.json create mode 100644 apps/web/public/file 2.svg create mode 100644 apps/web/public/globe 2.svg create mode 100644 apps/web/public/next 2.svg create mode 100644 apps/web/public/vercel 2.svg create mode 100644 apps/web/public/window 2.svg create mode 100644 apps/web/src/app/favicon 2.ico create mode 100644 apps/web/src/app/globals 2.css create mode 100644 apps/web/src/app/layout 2.tsx create mode 100644 apps/web/src/app/page 2.tsx create mode 100644 apps/web/tsconfig 2.json create mode 100644 apps/web/worker 2.ts create mode 100644 docs/decisions/017-identity 2.md create mode 100644 docs/decisions/022-cloudflare-paid-plan.md create mode 100644 eslint.config 2.mjs create mode 100644 package 2.json create mode 100644 packages/db/migrations/0003_resumable_sweep 2.sql create mode 100644 packages/db/migrations/meta/0003_snapshot 2.json create mode 100644 pnpm-lock 2.yaml create mode 100644 pnpm-workspace 2.yaml diff --git a/apps/web/.gitignore 2 b/apps/web/.gitignore 2 new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/apps/web/.gitignore 2 @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/apps/web/AGENTS 2.md b/apps/web/AGENTS 2.md new file mode 100644 index 0000000..af3b7c8 --- /dev/null +++ b/apps/web/AGENTS 2.md @@ -0,0 +1,9 @@ + + +## This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/apps/web/README 2.md b/apps/web/README 2.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/apps/web/README 2.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/apps/web/eslint.config 2.mjs b/apps/web/eslint.config 2.mjs new file mode 100644 index 0000000..df69b1a --- /dev/null +++ b/apps/web/eslint.config 2.mjs @@ -0,0 +1,22 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + // OpenNext and Wrangler output: + ".open-next/**", + ".wrangler/**", + "cloudflare-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/apps/web/next.config 2.ts b/apps/web/next.config 2.ts new file mode 100644 index 0000000..8bf865d --- /dev/null +++ b/apps/web/next.config 2.ts @@ -0,0 +1,18 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ + cacheComponents: true, + partialPrefetching: true, + reactCompiler: true, + turbopack: { + rules: { + "*.css": { + loaders: ["@tailwindcss/turbopack"], + as: "*.css", + }, + }, + }, +}; + +export default nextConfig; diff --git a/apps/web/package 2.json b/apps/web/package 2.json new file mode 100644 index 0000000..4975914 --- /dev/null +++ b/apps/web/package 2.json @@ -0,0 +1,31 @@ +{ + "name": "@gitwork/web", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint", + "typecheck": "next typegen && tsc --noEmit", + "cf:build": "opennextjs-cloudflare build", + "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview" + }, + "dependencies": { + "next": "16.4.2", + "react": "19.3.0", + "react-dom": "19.3.0" + }, + "devDependencies": { + "@tailwindcss/turbopack": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "babel-plugin-react-compiler": "1.0.0", + "eslint": "^9", + "eslint-config-next": "16.4.2", + "tailwindcss": "^4", + "typescript": "^5" + }, + "license": "AGPL-3.0-only" +} diff --git a/apps/web/public/file 2.svg b/apps/web/public/file 2.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/apps/web/public/file 2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/globe 2.svg b/apps/web/public/globe 2.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/apps/web/public/globe 2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/next 2.svg b/apps/web/public/next 2.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/apps/web/public/next 2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/vercel 2.svg b/apps/web/public/vercel 2.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/apps/web/public/vercel 2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/window 2.svg b/apps/web/public/window 2.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/apps/web/public/window 2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/app/favicon 2.ico b/apps/web/src/app/favicon 2.ico new file mode 100644 index 0000000000000000000000000000000000000000..af98450595e8b8efd9e505cddc5ed705b665a4d8 GIT binary patch literal 15086 zcmdU$XN*0PW5y#)gXL_h{3FkJ#X`oXpI?bB5#G)w~S7Qa+`W<1AB#X3C=?&UhvAO=|S+ zQpCR%R9`2h-z)7_x}fw(>6KDpfVd}*GI-!c2H9UDrKwS>=#NU@Ddj7@Q4*g7FEYrY z16}AW?v$zq+9+i!-B+T1{bG*9hmHqIUn`*-8^w`osM0?vVJi}rWb@IzKnYt#l~PpE zZq^t6O{HGFdQ!P^WzlxPHWGIHcd>D{}x={sTUg9&WW6602m zQnBH6>!yx}4jn2B7A%n2vuDesNt2{;dd zh7B@$^l0CFc@ZrPKrArsz@`@AQzbS#)CVarSbb!I8ty{PH zk}X}jv}9#v$)iV)Gb-#m>4qUO6)cT2PV3 zpI*Is$&Ww&XxZfC$&*s6RxM+zLWK&_s8J(BjQt6=fsGRsIez?@ix)2jvEROZTiUm8 zAGA}oYE|jjv7_)^<8Q|B8-jA4x_+h43HdwG`akQZ{rmR^u|IqEOoj~`7J{KpojS(< z>D#xjnZIoEL8L15HMT(z#WR80C=8eJ6m1l4LW@cu}s8OTj^Upsw@4?*W6SJES z?6iS9&)0pV`lp_$Z=3e*+h^9ZUK>rCG?5uIW|;4L`0(N0^@=YH0qjNg{kCh@PHx@0 zWueQ33l}2#^Q~LAHa;_LgYTK1p6(a3o6pAd+gv_56 zTeSG07MmF9|6)G^Q&#}`NMn_Um~~0isUI6)0h8ZU;`^Lf>f{vqND)`@P9?$9TK1fc;tCyQ6!+t!2dtUtke$_><;tw&q0@ECem?{wl!rS3|>b6OetkXT}@W?k_+rGrYh zl>Vi}m;6*0c#P>*l+@N$%>EBBFmQe{Fc#%OC9f_nmD)f~S=dohhj8vYl zv6`=x&=pVWsLX#+q8mig9W$TUxvt*}bzk7L-lNk`>Y+?Gm163T#zTyXvm=~)sb0N0 zaaweuJ6Zx`(zaEHG-%L3KK=Al6T>DZPhLQGq zy?Yqg@b-sP&7-d+WX~-}jDPy{>9TCuG9i8(A^wLgY}(XWZ-_f3WuJ2&?4j-5yH}Ph zSrTpU9$VNX@qMmWifEgC6yB+S|Ng&-_q*kY6P-PKR*oJ$D&4wui>NOkgH3F=P`Ve$ z3oFArk%&op&JP9oiCbR3eqHX}yJupw-hGKM`tJs8Gp0rz*J9ak*s!6TKY!j}B<@Qe z2xHHihfO=4d&@$equD1uh)vR!D_5jhvt}W(F>o+n#QfeP*=Ky4KYza26Uxue7xt`( zwGs1UFEoa2%CXJd6_q!V{lS9=%abQh(nAuU?8F#8U%^agA*f5vUU zO`A5Rzftd-H*XGp=j=}sgJvHomR>El^LJEU_xn@7oB=$3{5YoN^y$-PoVT`!UrwAj z(d>cRyeL=eJNUAXP0n?!TD8j9&dbY_&Ye4l$oA~n)9gP*+v9QJq3wP5KkQGLGDTj# zd};PI#*Q5uVjI0fh76HSn>LxVdoDY_;0u&tn{OaexyiTnfB*h{;fw|QLCcpf4;xpW zFkyme8_o^J(yQeMlzfjrrqcE2$dM!Fd_g$&SWBkEa^gaG!|Jt={ z=KFH#39++i(IT_Yx^(H%ko++GcKr1<|Ih~0rcI04uj!BQ`Z)V#=O2Y;=E9`s-!ShO zsSe!pFZPSf|L}>y>wkcu$o#Ju7{d8q*e4a4|Eapd)ji*PB>xlpq$2UZRgc#`wqyC< z*e8K8>Gg-|00V0nbj5maWeG|22l`3AlJ{NwJ|l|`ufMHXz7k^tYsg%g=)+?N8;M_9LtdS2KI3UcQZk+)cWFG|X4L^R?nKNgM zJp7#1#y{fx7Zcyn1ijH<*t+zGuIF{p8Z6OUCbF zJuqm{AfqFUdUSJ))2_z+s>OeFWelDY)Ji4$N2uccI^tDkzj4ezJD0s z6WNgVqu%*n4eJGq6%4dL{) { + return ( + + {children} + + ); +} diff --git a/apps/web/src/app/page 2.tsx b/apps/web/src/app/page 2.tsx new file mode 100644 index 0000000..f6e51de --- /dev/null +++ b/apps/web/src/app/page 2.tsx @@ -0,0 +1,10 @@ +import { APP_NAME } from "@gitwork/core"; + +export default function Home() { + return ( +
+

{APP_NAME}

+

GitHub is the source of truth.

+
+ ); +} diff --git a/apps/web/tsconfig 2.json b/apps/web/tsconfig 2.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/apps/web/tsconfig 2.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} diff --git a/apps/web/worker 2.ts b/apps/web/worker 2.ts new file mode 100644 index 0000000..6e3e019 --- /dev/null +++ b/apps/web/worker 2.ts @@ -0,0 +1,25 @@ +// Custom Worker entry (decision 004): OpenNext's fetch handler plus the reconcile Cron Trigger. +// OpenNext builds .open-next/worker.js; wrangler bundles this file around it. +import { openD1 } from "@gitwork/db/d1"; +import { runScheduledReconcile } from "@gitwork/server/sync"; +// Generated by `opennextjs-cloudflare build`; absent before the first build, so @ts-ignore. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import { default as handler } from "./.open-next/worker.js"; + +export default { + fetch: handler.fetch, + + async scheduled(_controller, env, ctx) { + ctx.waitUntil( + runScheduledReconcile(openD1(env.DB), env as unknown as Record, crypto.randomUUID()).then((run) => + console.log(`[reconcile] ${JSON.stringify(run)}`), + ), + ); + }, +} satisfies ExportedHandler; + +// Generated by `opennextjs-cloudflare build`; absent before the first build, so @ts-ignore. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +export { DOQueueHandler, DOShardedTagCache, BucketCachePurge } from "./.open-next/worker.js"; diff --git a/docs/INDEX.md b/docs/INDEX.md index 780e065..92c3ae6 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -30,6 +30,7 @@ | 019 | [Instance admins and email from the environment; wizard creates the GitHub App](decisions/019-instance-setup.md) | | 020 | [Linking a workspace is checked with the user's own GitHub token](decisions/020-workspace-link-boundary.md) | | 021 | [Task writes: user token first, App on refusal, synchronous](decisions/021-task-writes.md) | +| 022 | [Cloudflare hosting requires the Workers Paid plan](decisions/022-cloudflare-paid-plan.md) | ## Features diff --git a/docs/STATE.md b/docs/STATE.md index bd9bdc3..b495e1e 100644 --- a/docs/STATE.md +++ b/docs/STATE.md @@ -1,11 +1,10 @@ # State -Last updated: 2026-10-15 +Last updated: 2026-10-16 ## Resume here -- **Phase 3 (tasks on web) is in review** on branch `feature/tasks` (PR pending owner merge). - PRs #1–#3 (Phases 0–2) are merged. +- PRs #1–#4 (Phases 0–3) are merged. Next: Phase 4 — Docs on web. - **Live GitHub check: still not done.** It needs a person (or a connected browser) to click "Create GitHub App" on github.com, and a public webhook URL. See Chores. - **Before any work:** `git fetch origin && git status -sb`. @@ -24,7 +23,7 @@ None. Next step: Phase 4 — Docs on web. | Phase 0 — Scaffold | 2026-10-15 | [features/scaffold.md](features/scaffold.md) | | Phase 1 — Identity | 2026-10-15 | [features/identity.md](features/identity.md) | | Phase 2 — GitHub App (PR #3) | 2026-10-15 | [features/github-app.md](features/github-app.md) | -| Phase 3 — Tasks on web (in review) | 2026-10-15 | [features/tasks.md](features/tasks.md) | +| Phase 3 — Tasks on web (PR #4) | 2026-10-16 | [features/tasks.md](features/tasks.md) | ## Backlog @@ -42,10 +41,7 @@ Chores: (`GET /app/hook/deliveries/{id}`) and replay them to the local webhook route, re-signed with the stored secret (the API returns parsed JSON, not the signed bytes). Diff real payloads against `test/fake-github.ts`; exercise linking and a task write with a real user token. -- Workers bundle is 3.40 MiB gzipped, over the Free plan's 3 MiB (main was already 3.23 MiB). - Decide: require the Paid plan for Cloudflare, or cut the bundle. -- Owner question (decision 020): should a workspace unlink when the Admin who linked it loses - GitHub access? +- `pnpm smoke`: fail when the gzipped Worker bundle passes 10 MiB (decision 022). - Before a hosted launch: `/github/installed` should show org names only to users whose GitHub token can see the installation (now it shows none). - Upgrade Next 16.3.8 → 16.4.x with OpenNext ≥1.20.12 once it passes the pnpm age policy. diff --git a/docs/decisions/009-tasks-projects-v2.md b/docs/decisions/009-tasks-projects-v2.md index b5a0868..9b63ef0 100644 --- a/docs/decisions/009-tasks-projects-v2.md +++ b/docs/decisions/009-tasks-projects-v2.md @@ -21,3 +21,5 @@ Projects v2 as the task source. ## Consequences - Requires the org Projects permission, so org-owned boards only (006). - Projects v2 webhooks are in public preview; reconcile covers gaps (010). +- Field mapping has "Auto-detect" (by GitHub's usual field names) but no "none": a mapped field can + be overridden, not switched off. (Owner confirmed 2026-10-16.) diff --git a/docs/decisions/017-identity 2.md b/docs/decisions/017-identity 2.md new file mode 100644 index 0000000..21d3339 --- /dev/null +++ b/docs/decisions/017-identity 2.md @@ -0,0 +1,34 @@ +# 017 — Identity model, GitHub gate, sign-up policy + +Date: 2026-10-15 · Status: Accepted · Narrows 007 · Supersedes part of 005 + +## Context +Phase 1 builds sign-in, accounts, workspaces, roles, and invites. Three questions came up: what +Better Auth should own, which Owner/Admin capabilities need a linked GitHub account, and who may +create a new Account. + +## Decision +- **Better Auth owns identity only:** users, sessions, verification codes, linked GitHub identities. + Gitwork owns accounts, account owners, workspaces, memberships, and invites in its own tables. + Better Auth's `organization` plugin is not used. Better Auth's "account" model is stored as + `auth_identities` so it never collides with Gitwork's Account. +- **GitHub gate (owner decision):** a linked GitHub account is needed only for GitHub tasks — + installing the App and setting a workspace's repos, board, and fields. Owners and Admins can + invite, manage members, and change other settings without GitHub. The gated set is one constant, + `GITHUB_CAPABILITIES`, in `packages/core`. +- **Sign-up policy (owner decision):** an instance setting, `open` or `invite-only`. Default `open` + until the Phase 2 setup wizard exists; then self-host defaults to `invite-only` and the hosted + version stays `open`. A person with a valid pending invite can always sign up. Joining a + workspace always needs an invite. + +## Alternatives considered +- **Better Auth `organization` plugin:** has roles and invites, but no Account level above + workspaces and no GitHub gate. +- **Gate all Owner/Admin power on GitHub:** a new Owner could not invite anyone before linking. +- **Always open / always invite-only sign-up:** the first suits hosting but not private installs; + the second leaves the hosted version no way to onboard customers. + +## Consequences +- Every permission check calls `packages/core`; the matrix tests cover the gate. +- The sign-up check is a hook before user creation, not Better Auth's global `disableSignUp`, which + would lock out invitees. diff --git a/docs/decisions/020-workspace-link-boundary.md b/docs/decisions/020-workspace-link-boundary.md index 97ab9f7..d154026 100644 --- a/docs/decisions/020-workspace-link-boundary.md +++ b/docs/decisions/020-workspace-link-boundary.md @@ -1,6 +1,6 @@ # 020 — Linking a workspace is checked with the user's own GitHub token -Date: 2026-10-15 · Status: Accepted · Builds on 006, 017 +Date: 2026-10-15 · Status: Accepted (owner confirmed 2026-10-16) · Builds on 006, 017 ## Context A workspace links one board and a list of repos (006). The App's installation token can read every @@ -32,5 +32,5 @@ pull in. Without a check, an Admin of one workspace could link another customer' ## Consequences - The link reflects what the linking Admin could see **when they saved it**. If they later lose access, the workspace keeps its repos (the org's installation still covers them). Re-linking - re-checks. Open question for the owner: should losing access unlink? + re-checks. (Owner confirmed 2026-10-16: keep the link.) - Pull requests on the board are not tasks in v1; they are filtered out like any unknown type. diff --git a/docs/decisions/021-task-writes.md b/docs/decisions/021-task-writes.md index 9e7f884..e4b5dc2 100644 --- a/docs/decisions/021-task-writes.md +++ b/docs/decisions/021-task-writes.md @@ -1,6 +1,6 @@ # 021 — Task writes: user token first, App on refusal, synchronous -Date: 2026-10-15 · Status: Accepted · Builds on 007, 008, 009 +Date: 2026-10-15 · Status: Accepted (owner confirmed the defaults 2026-10-16) · Builds on 007, 008, 009 ## Context Decision 008 says linked users act as themselves and everyone else writes through the App. Two diff --git a/docs/decisions/022-cloudflare-paid-plan.md b/docs/decisions/022-cloudflare-paid-plan.md new file mode 100644 index 0000000..3fa00f1 --- /dev/null +++ b/docs/decisions/022-cloudflare-paid-plan.md @@ -0,0 +1,22 @@ +# 022 — Cloudflare hosting requires the Workers Paid plan + +Date: 2026-10-16 · Status: Accepted (owner) · Builds on 001, 002 + +## Context +The Worker bundle is 3.40 MiB gzipped after Phase 3 (3.23 MiB after Phase 2). The Workers Free +plan allows 3 MiB per Worker; the Paid plan allows 10 MiB. Free also caps subrequests at 50 per +invocation, which the reconcile defaults already exceed. + +## Decision +Deploying Gitwork to Cloudflare requires the Workers Paid plan. We do not cut the bundle to fit +the Free plan. Node hosts (decision 001) have no such limit. + +## Alternatives considered +- **Cut the bundle under 3 MiB:** drop or lazy-load dependencies (Better Auth plugins, Radix, + drizzle). Costly, and every phase adds code; the limit would bind again soon. +- **Support Free with reduced features:** two configurations to test for a small saving. + +## Consequences +- The self-host guide (Phase 7) and the Deploy-to-Cloudflare button state the Paid plan. +- `pnpm smoke` should fail when the gzipped bundle passes 10 MiB (follow-up). +- Reconcile defaults (`RECONCILE_MAX_PAGES`) stay sized for the Paid plan. diff --git a/docs/features/tasks.md b/docs/features/tasks.md index 1f8fcfe..36f520a 100644 --- a/docs/features/tasks.md +++ b/docs/features/tasks.md @@ -62,7 +62,7 @@ content, so board syncs and `projects_v2_item` refetches keep `gh_issue_links` c payload (`IssueLinks` query) instead of trusting it. **Field mapping.** Stored ids win when they still fit the field type. A stored null means -**auto-detect** (the settings form labels it so); there is no "none" yet. Auto-detect: kanban = a +**auto-detect** (the settings form labels it so); there is no "none" (decision 009). Auto-detect: kanban = a single-select named "Status", else the first one; start = "Start date"/"Start"; end = "Target date"/"End date"/"Due date"/"Due"; iteration = the first iteration field. @@ -108,8 +108,8 @@ Add components with `pnpm exec shadcn add ` from `apps/web`. - **D1 test reset** drops tables in foreign-key order, read from the `CREATE` statements (D1 refuses `pragma_foreign_key_list`). drizzle-kit creates tables alphabetically, so neither name nor creation order is a dependency order. -- **Worker bundle:** 3.40 MiB gzipped (main was 3.23 MiB before this phase). Both exceed the Workers - Free plan's 3 MiB; the Paid plan allows 10 MiB. Self-hosters on Free cannot deploy as is. +- **Worker bundle:** 3.40 MiB gzipped (3.23 MiB before this phase), over the Workers Free plan's + 3 MiB. Cloudflare hosting requires the Paid plan (decision 022). - **Not tested against real GitHub.** `/user/installations/{id}/repositories`, the Projects v2 mutations, and the `sub_issues` / `issue_dependencies` payload keys follow GitHub's docs and the live GraphQL schema (introspected 2026-10-15), not real calls. See STATE → live check. diff --git a/docs/spec.md b/docs/spec.md index f960965..8dd74e8 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -52,6 +52,7 @@ Each has a record in `docs/decisions/`. | 019 | Instance admins (`INSTANCE_ADMIN_EMAILS`) and email come from env; `/setup` creates the GitHub App. | | 020 | Linking a board and repos is checked with the linking user's own GitHub token; one installation per workspace. | | 021 | Task writes: user token first, the App when GitHub refuses; synchronous; audit row per write. | +| 022 | Cloudflare hosting requires the Workers Paid plan (bundle over 3 MiB). | ## Roles diff --git a/eslint.config 2.mjs b/eslint.config 2.mjs new file mode 100644 index 0000000..df5f30c --- /dev/null +++ b/eslint.config 2.mjs @@ -0,0 +1,8 @@ +// Lint config for packages/*. apps/web has its own (eslint-config-next). +import { defineConfig, globalIgnores } from "eslint/config"; +import tseslint from "typescript-eslint"; + +export default defineConfig([ + globalIgnores(["**/node_modules/**", "apps/**", "packages/db/migrations/**"]), + ...tseslint.configs.recommended, +]); diff --git a/package 2.json b/package 2.json new file mode 100644 index 0000000..21bc790 --- /dev/null +++ b/package 2.json @@ -0,0 +1,16 @@ +{ + "name": "gitwork", + "private": true, + "license": "AGPL-3.0-only", + "packageManager": "pnpm@12.11.2", + "engines": { + "node": ">=22" + }, + "scripts": { + "dev": "pnpm --filter @gitwork/web dev", + "build": "pnpm -r build", + "test": "vitest run", + "lint": "pnpm -r lint", + "typecheck": "pnpm -r typecheck" + } +} diff --git a/packages/db/migrations/0003_resumable_sweep 2.sql b/packages/db/migrations/0003_resumable_sweep 2.sql new file mode 100644 index 0000000..e43c461 --- /dev/null +++ b/packages/db/migrations/0003_resumable_sweep 2.sql @@ -0,0 +1,2 @@ +ALTER TABLE `gh_sync_targets` ADD `sweep_started_at` integer;--> statement-breakpoint +ALTER TABLE `gh_sync_targets` ADD `sweep_cursor` text; \ No newline at end of file diff --git a/packages/db/migrations/meta/0003_snapshot 2.json b/packages/db/migrations/meta/0003_snapshot 2.json new file mode 100644 index 0000000..b687cf2 --- /dev/null +++ b/packages/db/migrations/meta/0003_snapshot 2.json @@ -0,0 +1,1777 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "62143336-6275-4b95-9cab-dc2f238bb5dd", + "prevId": "b1f08e6d-32b5-4e16-b855-0d076c3c89b3", + "tables": { + "account_owners": { + "name": "account_owners", + "columns": { + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "account_owners_user_id_idx": { + "name": "account_owners_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "account_owners_account_id_accounts_id_fk": { + "name": "account_owners_account_id_accounts_id_fk", + "tableFrom": "account_owners", + "tableTo": "accounts", + "columnsFrom": [ + "account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "account_owners_user_id_users_id_fk": { + "name": "account_owners_user_id_users_id_fk", + "tableFrom": "account_owners", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_owners_account_id_user_id_pk": { + "columns": [ + "account_id", + "user_id" + ], + "name": "account_owners_account_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "auth_identities": { + "name": "auth_identities", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "auth_identities_user_id_idx": { + "name": "auth_identities_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "auth_identities_provider_account_uq": { + "name": "auth_identities_provider_account_uq", + "columns": [ + "provider_id", + "account_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "auth_identities_user_id_users_id_fk": { + "name": "auth_identities_user_id_users_id_fk", + "tableFrom": "auth_identities", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "gh_issues": { + "name": "gh_issues", + "columns": { + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "repo_node_id": { + "name": "repo_node_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "state_reason": { + "name": "state_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "labels": { + "name": "labels", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "assignees": { + "name": "assignees", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "author_login": { + "name": "author_login", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "issue_type": { + "name": "issue_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "html_url": { + "name": "html_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at_gh": { + "name": "created_at_gh", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "closed_at_gh": { + "name": "closed_at_gh", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_updated_at": { + "name": "github_updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deleted": { + "name": "deleted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "synced_at": { + "name": "synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "gh_issues_repo_number_uq": { + "name": "gh_issues_repo_number_uq", + "columns": [ + "repo_node_id", + "number" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "gh_project_fields": { + "name": "gh_project_fields", + "columns": { + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_node_id": { + "name": "project_node_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "data_type": { + "name": "data_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config": { + "name": "config", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "synced_at": { + "name": "synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "gh_project_fields_project_idx": { + "name": "gh_project_fields_project_idx", + "columns": [ + "project_node_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "gh_project_items": { + "name": "gh_project_items", + "columns": { + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_node_id": { + "name": "project_node_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_node_id": { + "name": "content_node_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "draft_title": { + "name": "draft_title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "draft_body": { + "name": "draft_body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "field_values": { + "name": "field_values", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "archived": { + "name": "archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "github_updated_at": { + "name": "github_updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deleted": { + "name": "deleted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "synced_at": { + "name": "synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "gh_project_items_project_idx": { + "name": "gh_project_items_project_idx", + "columns": [ + "project_node_id" + ], + "isUnique": false + }, + "gh_project_items_content_idx": { + "name": "gh_project_items_content_idx", + "columns": [ + "content_node_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "gh_projects": { + "name": "gh_projects", + "columns": { + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "installation_id": { + "name": "installation_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_login": { + "name": "owner_login", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "closed": { + "name": "closed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "available": { + "name": "available", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "synced_at": { + "name": "synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "gh_repos": { + "name": "gh_repos", + "columns": { + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "github_id": { + "name": "github_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "installation_id": { + "name": "installation_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private": { + "name": "private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "archived": { + "name": "archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "html_url": { + "name": "html_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "available": { + "name": "available", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "synced_at": { + "name": "synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "gh_repos_github_id_unique": { + "name": "gh_repos_github_id_unique", + "columns": [ + "github_id" + ], + "isUnique": true + }, + "gh_repos_installation_idx": { + "name": "gh_repos_installation_idx", + "columns": [ + "installation_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "gh_sync_targets": { + "name": "gh_sync_targets", + "columns": { + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "installation_id": { + "name": "installation_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locator": { + "name": "locator", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_full_sync_at": { + "name": "last_full_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sweep_started_at": { + "name": "sweep_started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sweep_cursor": { + "name": "sweep_cursor", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "gh_sync_targets_kind_node_id_pk": { + "columns": [ + "kind", + "node_id" + ], + "name": "gh_sync_targets_kind_node_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": { + "gh_sync_targets_kind_ck": { + "name": "gh_sync_targets_kind_ck", + "value": "\"gh_sync_targets\".\"kind\" IN ('repo', 'project')" + } + } + }, + "github_app": { + "name": "github_app", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "app_id": { + "name": "app_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "webhook_secret": { + "name": "webhook_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "html_url": { + "name": "html_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_login": { + "name": "owner_login", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "github_app_singleton_ck": { + "name": "github_app_singleton_ck", + "value": "\"github_app\".\"id\" = 1" + } + } + }, + "github_installation_tokens": { + "name": "github_installation_tokens", + "columns": { + "installation_id": { + "name": "installation_id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "github_installations": { + "name": "github_installations", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_login": { + "name": "account_login", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "account_type": { + "name": "account_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_selection": { + "name": "repository_selection", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "github_manifest_states": { + "name": "github_manifest_states", + "columns": { + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used_at": { + "name": "used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "github_manifest_states_user_id_users_id_fk": { + "name": "github_manifest_states_user_id_users_id_fk", + "tableFrom": "github_manifest_states", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "github_webhook_deliveries": { + "name": "github_webhook_deliveries", + "columns": { + "delivery_id": { + "name": "delivery_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "installation_id": { + "name": "installation_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "instance_settings": { + "name": "instance_settings", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invites": { + "name": "invites", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "accepted_by": { + "name": "accepted_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "invites_token_hash_unique": { + "name": "invites_token_hash_unique", + "columns": [ + "token_hash" + ], + "isUnique": true + }, + "invites_workspace_id_idx": { + "name": "invites_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "invites_email_idx": { + "name": "invites_email_idx", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": { + "invites_workspace_id_workspaces_id_fk": { + "name": "invites_workspace_id_workspaces_id_fk", + "tableFrom": "invites", + "tableTo": "workspaces", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invites_invited_by_users_id_fk": { + "name": "invites_invited_by_users_id_fk", + "tableFrom": "invites", + "tableTo": "users", + "columnsFrom": [ + "invited_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "invites_accepted_by_users_id_fk": { + "name": "invites_accepted_by_users_id_fk", + "tableFrom": "invites", + "tableTo": "users", + "columnsFrom": [ + "accepted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "invites_role_ck": { + "name": "invites_role_ck", + "value": "\"invites\".\"role\" IN ('owner', 'admin', 'member', 'read_only')" + }, + "invites_email_lower_ck": { + "name": "invites_email_lower_ck", + "value": "\"invites\".\"email\" = lower(\"invites\".\"email\")" + } + } + }, + "rate_limits": { + "name": "rate_limits", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_request": { + "name": "last_request", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "rate_limits_key_unique": { + "name": "rate_limits_key_unique", + "columns": [ + "key" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "sessions_user_id_idx": { + "name": "sessions_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sync_leases": { + "name": "sync_leases", + "columns": { + "name": { + "name": "name", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "holder": { + "name": "holder", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "verifications_identifier_idx": { + "name": "verifications_identifier_idx", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workspace_members": { + "name": "workspace_members", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "workspace_members_user_id_idx": { + "name": "workspace_members_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspace_members_workspace_id_workspaces_id_fk": { + "name": "workspace_members_workspace_id_workspaces_id_fk", + "tableFrom": "workspace_members", + "tableTo": "workspaces", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_members_user_id_users_id_fk": { + "name": "workspace_members_user_id_users_id_fk", + "tableFrom": "workspace_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_members_workspace_id_user_id_pk": { + "columns": [ + "workspace_id", + "user_id" + ], + "name": "workspace_members_workspace_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": { + "workspace_members_role_ck": { + "name": "workspace_members_role_ck", + "value": "\"workspace_members\".\"role\" IN ('owner', 'admin', 'member', 'read_only')" + } + } + }, + "workspaces": { + "name": "workspaces", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read_only_can_comment": { + "name": "read_only_can_comment", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "show_drafts_to_read_only": { + "name": "show_drafts_to_read_only", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "workspaces_account_id_idx": { + "name": "workspaces_account_id_idx", + "columns": [ + "account_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspaces_account_id_accounts_id_fk": { + "name": "workspaces_account_id_accounts_id_fk", + "tableFrom": "workspaces", + "tableTo": "accounts", + "columnsFrom": [ + "account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/pnpm-lock 2.yaml b/pnpm-lock 2.yaml new file mode 100644 index 0000000..fb156bc --- /dev/null +++ b/pnpm-lock 2.yaml @@ -0,0 +1,167 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.11.2 + version: 12.11.2 + +packages: + + '@pnpm/exe.android-arm64@12.11.2': + resolution: {integrity: sha512-owLw13OfPkEoY+hLBNeeG+dCFhywr7q2d/EHPz4mNL2RhKnv8gNwMARkjkiDQu234aItVwFNDeGnmQXm+ikkAg==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.11.2': + resolution: {integrity: sha512-GYPkVebl4iokOA/4MPSpGoUVfFlsth7uqOCe1XiaiM23LAdvUOMJBObqHJijr89ZqjwHm9pO6vO45GRwJ2U2qA==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.11.2': + resolution: {integrity: sha512-n9M4On66y8FDxUAKgMO1FXt0qihyG4e+56p3/F0uo8Bx9KplW8BwKVnXigzCJ8aNM6sVVPkOE6M9XXbeVIxnZA==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.11.2': + resolution: {integrity: sha512-oSW7n3sBT0c0cyk4kj10T2Y6Mpm+OFEiCAWozgX039tdoI6yqJF1uaFD3sw7ugLdu6VV3+uY0pa1TUZIPdxDWA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.11.2': + resolution: {integrity: sha512-qbzc3KoDpa/Lkf8vs3jnDe6dUUbso+6eLIyssYFMiliLpYNxn8dirk11YoNnOQcdHVB50v5RFls1Yid6ttT5pg==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.11.2': + resolution: {integrity: sha512-uMEYDwKRktGwANubQ+kYCwmhNwg3Ik6qK89ahsbQ5cP30R/5FVJb/T99vr9XRUBC/A7mZI6eeT92oyVas9ACyg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.11.2': + resolution: {integrity: sha512-6e8ZAQUwFXAWHHHv5FesWqf/kEK04wuitBv5f/gxzkC0DiBfaixr+f+6JQede8SF4Ji5LcMwVFJbzAtffpWxEg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.11.2': + resolution: {integrity: sha512-Ui8A4nepkVupx2VpuFfysEluMvWK6gTB5rgVts7Ss/+8dj0RD28x3F/seNeH/zSypgC6yKdV6qsKVQh/uffTuQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.11.2': + resolution: {integrity: sha512-i1yLdQ5bu+wjQ+srtFoJAaY2gN/+oXHFdjul4llCeoDxHE0rTIG3R41NhHfCjyFPW4+CpbBT58933CL9sMOCnA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.11.2': + resolution: {integrity: sha512-YS1/lhPjjTsN4QL50VaFZKRa3Uc/bDnhE8eKbJMFvuX06UuFS5YaiDg04APoZdfVSGqTfX27QAg7rfLMSWaqxA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.11.2': + resolution: {integrity: sha512-7eIxQR05fvJxCujUSKp5KTtehJ7QqrgqP/HD1+BcB/SaV2Be4XI1PEHiOgA1zxsiMIeMvTF6E4G4dYHrFjeXRg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.11.2': + resolution: {integrity: sha512-FK7QKe24AYgppJwO3mpooYptduuvIGqKJPi5ac7MdnWkjUy46JBfYhCW0grnkWKC8YOf0x+vGviuQqJEj3SpQQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.11.2': + resolution: {integrity: sha512-PCTjVDdJb6VvraZxrEeFv6EqEA0hhIsAySx28e95J2Tqpi88hLpgOXgNqV0vpxq9A+b8GLQfyk/D+1UlJPv/YQ==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.11.2': + resolution: {integrity: sha512-8sgX86OvRjJ3yZs/TvGR2L31wH9ScPZ+f+geKI3ikuB0RRiK3i4LlTRO8OXN6pdRqj8epMde4312MCjj7JsJdA==} + cpu: [x64] + os: [win32] + + pnpm@12.11.2: + resolution: {integrity: sha512-JaUWUqGWeFr18ovabf6NxYLr76SsHRgCIc4TkCMcX7dmN4IupLbkYzznSs3LCnf3Ja8RutOBd/3t2u6BipVcuQ==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.11.2': + optional: true + + '@pnpm/exe.android-x64@12.11.2': + optional: true + + '@pnpm/exe.darwin-arm64@12.11.2': + optional: true + + '@pnpm/exe.darwin-x64@12.11.2': + optional: true + + '@pnpm/exe.freebsd-x64@12.11.2': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.11.2': + optional: true + + '@pnpm/exe.linux-arm64@12.11.2': + optional: true + + '@pnpm/exe.linux-ppc64@12.11.2': + optional: true + + '@pnpm/exe.linux-riscv64@12.11.2': + optional: true + + '@pnpm/exe.linux-s390x@12.11.2': + optional: true + + '@pnpm/exe.linux-x64-musl@12.11.2': + optional: true + + '@pnpm/exe.linux-x64@12.11.2': + optional: true + + '@pnpm/exe.win32-arm64@12.11.2': + optional: true + + '@pnpm/exe.win32-x64@12.11.2': + optional: true + + pnpm@12.11.2: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.11.2 + '@pnpm/exe.android-x64': 12.11.2 + '@pnpm/exe.darwin-arm64': 12.11.2 + '@pnpm/exe.darwin-x64': 12.11.2 + '@pnpm/exe.freebsd-x64': 12.11.2 + '@pnpm/exe.linux-arm64': 12.11.2 + '@pnpm/exe.linux-arm64-musl': 12.11.2 + '@pnpm/exe.linux-ppc64': 12.11.2 + '@pnpm/exe.linux-riscv64': 12.11.2 + '@pnpm/exe.linux-s390x': 12.11.2 + '@pnpm/exe.linux-x64': 12.11.2 + '@pnpm/exe.linux-x64-musl': 12.11.2 + '@pnpm/exe.win32-arm64': 12.11.2 + '@pnpm/exe.win32-x64': 12.11.2 + +--- +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} diff --git a/pnpm-workspace 2.yaml b/pnpm-workspace 2.yaml new file mode 100644 index 0000000..df7cc6e --- /dev/null +++ b/pnpm-workspace 2.yaml @@ -0,0 +1,9 @@ +packages: + - "apps/*" + - "packages/*" + +allowBuilds: + esbuild: true + workerd: true + sharp: false + unrs-resolver: false