ข้ามไปยังเนื้อหา
Pre-MVP โค้ดเบส 1.0 ยังไม่ใช่ผลิตภัณฑ์ที่เผยแพร่ — ดู ขอบเขตผลิตภัณฑ์ 1.0

การต่อยอดแบ็กเอนด์ของคุณ

คุณได้สร้างแบ็กเอนด์แบบ standalone จากไซต์หนึ่งแล้ว มันเป็น repository ปกติ และ ไม่ import สิ่งใดจาก CMS ในขณะ runtime เอกสารนี้บอกว่าโค้ดของคุณควรอยู่ที่ใด การสร้างใหม่ (regeneration) เขียนทับอะไร และปฏิเสธอะไร

คำตอบสั้น ๆ: src/owned/ เป็นของคุณตลอดไป apps/site/src/owned/ และ apps/site/src/pages/owned/ เป็นของคุณในไซต์ที่ถูกส่งออก package.json, wrangler.jsonc, .nvmrc, .gitignore, README.md และ workflow ของคุณเอง เป็นของคุณที่จะเก็บไว้ ทุกอย่างที่เหลือจะถูกเขียนใหม่จากนิยามโมเดลในทุกครั้งที่ สร้างใหม่

ที่มาของกฎนี้: backend/src/lib/graduate/zones.ts ตารางด้านล่างถูก ตรวจสอบเทียบกับโค้ดนั้นโดย backend/test/extending-your-backend-doc.test.ts ดังนั้นเอกสารนี้จึงไม่สามารถคลาดเคลื่อนไปจาก emitter ได้

สิ่งที่คุณเป็นเจ้าของ และสิ่งที่การสร้างใหม่เขียนทับ

หัวข้อที่มีชื่อว่า “สิ่งที่คุณเป็นเจ้าของ และสิ่งที่การสร้างใหม่เขียนทับ”

แบ็กเอนด์ที่สร้างขึ้นมีไฟล์สามประเภท กฎจะตัดสินโซนจาก PATH เพียงอย่างเดียว — ไม่ใช่จาก comment ที่เป็นเครื่องหมาย และไม่ใช่จากรายการที่ใครสักคนดูแล

โซน ใครเป็นเจ้าของ การสร้างใหม่ทำอะไร
generated emitter เขียนใหม่ทุกครั้งที่รัน
owned นักพัฒนา เขียนครั้งเดียวเป็น scaffold และไม่แตะอีกเลย
shared ทั้งสองฝ่าย เขียนเมื่อยังไม่มีไฟล์ และไม่เขียนทับเด็ดขาด

มีข้อยกเว้นหนึ่งข้อ ซึ่งกำหนดตาม key: package.json — ทั้งไฟล์ที่ root และไฟล์ใน apps/site/ — จะถูก MERGE ไม่ใช่ปล่อยไว้เฉย ๆ ดู “การ merge manifest”

กฎที่แน่นอนเป็นดังนี้ คำถามเกี่ยวกับไซต์จะถูกถามก่อน (FIRST) เพราะ apps/site/package.json ใช้ชื่อแบบ shared ที่ API ก็ใช้เช่นกัน และ apps/site/src/owned/x ไม่ได้ขึ้นต้นด้วย src/owned/:

  • path ที่ขึ้นต้นด้วย apps/site/src/owned/ หรือ apps/site/src/pages/owned/ เป็น owned
  • path ที่เป็น apps/site/package.json, apps/site/package-lock.json, apps/site/.npmrc หรือ apps/site/wrangler.jsonc เป็น shared
  • สิ่งอื่นใดภายใต้ apps/site/ เป็น generated
  • path ที่ขึ้นต้นด้วย src/owned/ เป็น owned
  • path ที่เป็น .github/workflows/dee-wan-build.yml หรือ .github/workflows/deploy-from-repository.yml เป็น generated ทั้งสองเป็น การกำหนดค่าของเครื่อง การแก้ไขไฟล์เหล่านี้ต้องไปถึงโปรเจกต์ที่ถูกรับไปดูแลแล้ว
  • path ที่เป็น package.json, package-lock.json, .npmrc, .nvmrc, wrangler.jsonc, .gitignore, README.md หรือที่ขึ้นต้นด้วย .github/ เป็น shared
  • ทุกอย่างที่เหลือเป็น generated

มีสอง path ที่ไม่ถูกสแกนเลย ดังนั้นจึงไม่ถูกทำ fingerprint และไม่ถูก stage เด็ดขาด: src/serve.local.mjs และ src/credential-digest.mjs ทั้งสองเป็นผลลัพธ์ของ BUILD (BUILD OUTPUT) ที่อยู่ใน src/ เพราะช่วยให้ Prisma client ที่สร้างขึ้นอยู่ภายนอก bundle ทั้งสองอยู่ใน .gitignore — การเสนอไฟล์เหล่านี้ให้ git add เคยทำให้การ push ทั้งหมดล้มเหลว

ไฟล์ generated ที่ถูกแก้ไขจะถูกปฏิเสธโดยระบุชื่อ (REFUSED BY NAME) ในการสร้างใหม่ครั้งถัดไป พร้อมบอกที่ ที่ควรย้ายงานไป มันจะไม่ถูกเขียนทับแบบเงียบ ๆ เด็ดขาด ดู “สิ่งที่ถูกปฏิเสธ”

Path โซน
prisma/schema.prisma generated
graduation.manifest.json generated
src/app.ts generated
src/worker.ts generated
src/security.ts generated
src/owned/routes.ts owned
src/serve-cli.ts generated
migrations/0002_graduation_ledger.sql generated
scripts/regenerate.mjs generated
scripts/emit-migration.mjs generated
scripts/file-manifest.mjs generated
scripts/portable-client.mjs generated
scripts/configure-deployment.mjs generated
tsconfig.json generated
wrangler.example.jsonc generated
wrangler.build.jsonc generated
scripts/load-snapshot.mjs generated
data/published-snapshot.json generated
.github/workflows/dee-wan-build.yml generated
.github/workflows/deploy-from-repository.yml generated
.github/workflows/anything-else.yml shared
package.json shared
package-lock.json shared
.npmrc shared
.nvmrc shared
wrangler.jsonc shared
.gitignore shared
README.md shared
apps/site/src/owned/config.mjs owned
apps/site/src/pages/owned/thanks.astro owned
apps/site/package.json shared
apps/site/package-lock.json shared
apps/site/.npmrc shared
apps/site/wrangler.jsonc shared
apps/site/src/lib/pages.json generated

src/owned/routes.ts เป็น scaffold ที่เขียนครั้งเดียว และถูกส่งมาแบบว่างเปล่า (EMPTY) โดยตั้งใจ ใส่ route ของคุณไว้ที่นั่น:

export function ownedRoutes(prisma: GuardedPrisma) {
const routes = new Hono()
routes.post('/checkout', async (c) => {
const body = await c.req.json()
return c.json({ ok: true })
})
return routes
}

src/app.ts mount มันด้วย app.route('/api', ownedRoutes(deps.prisma)) เป็นลำดับสุดท้าย (LAST) — หลังจาก router ของทุกโมเดล ดังนั้น path ที่คุณเลือกจะไม่มีทางบัง path ที่สร้างขึ้นได้ /checkout ของคุณข้างต้นจะให้บริการที่ /api/checkout

สิ่งที่คุณได้รับสืบทอดมา: การยืนยันตัวตนทำงานบน app.use('*') ก่อนที่จะมีสิ่งใดถูก mount ดังนั้นทุก request ที่มาถึงคุณได้ถูกระบุตัวตนหรือถูกปฏิเสธไปแล้ว ภายใน handler คุณมี c.get('principal'), c.get('accessVariant') และ c.get('prisma') client ที่มี guard ถูกส่งเข้ามา (PASSED IN) แทนที่จะถูก import ดังนั้น query ของคุณจะทำงานภายใต้ extension เดียวกับที่ router ที่สร้างขึ้นใช้

สิ่งที่คุณต้องไม่สันนิษฐาน — การตรวจสอบสิทธิ์จะถามว่า request นั้นเป็นของ MODEL ใด และตอบโดยใช้ PREFIX /api/<model>…:

  1. path ที่ขึ้นต้นด้วยชื่อของโมเดลจะถูกตรวจสอบสิทธิ์โดยถือว่าเป็นโมเดลนั้น (AS THAT MODEL) /api/articles-report ตรงกับโมเดล article ดังนั้นผู้เรียกต้องมีสิทธิ์ของ article จึงจะเข้าถึงได้ — มากหรือน้อยกว่าที่คุณตั้งใจไว้
  2. path ที่ไม่ตรงกับโมเดลใดจะ resolve ไปยังไม่มีโมเดล ผู้เรียกที่ใช้ session จะถูก ควบคุมโดย operation เพียงอย่างเดียว (GET, POST…) ส่วนผู้เรียกที่ใช้ read token จะถูกปฏิเสธ เว้นแต่ scope ของมันคือ *

ดังนั้นให้เลือก segment แรกที่ไม่มี slug ของโมเดลใดใช้ได้ — /checkout, /webhooks/stripe, /reports/daily หาก route ต้องการกฎของตัวเอง ให้บังคับใช้ ใน handler ไม่มีสิ่งใดในนั้นถูกตรวจสอบให้คุณ

สำหรับ route ของไซต์แทนที่จะเป็น route ของ API ให้เพิ่มไฟล์ภายใต้ apps/site/src/pages/owned/ route ของ Astro มีอยู่ได้ด้วยการเป็นไฟล์ภายใต้ src/pages ดังนั้นหน้าที่เป็น owned จึงอยู่ใน apps/site/src/owned/ ไม่ได้ หน้านั้นจะให้บริการ ภายใต้ /owned/… และ address resolver จะปฏิเสธหน้าที่สร้างขึ้น, chrome route หรือ redirect ใด ๆ ที่จะบังพื้นที่ URL นั้น

ที่เดียวกัน จะเป็นไฟล์ใดก็ได้: ทั้ง tree ของ src/owned/ เป็นของคุณ เพิ่ม src/owned/pricing.ts แล้ว import จาก src/owned/routes.ts ใส่สิ่งเหล่านี้ไว้ที่นั่น:

  • route ของคุณเอง,
  • business logic,
  • การยืนยันตัวตนเพิ่มเติมนอกเหนือจากการตรวจสอบ bearer ที่ถูกส่งออก,
  • การเชื่อมต่อกับระบบของบุคคลที่สาม,
  • ตารางของคุณเองและ migration ของตาราง,
  • ทุกสิ่งที่ต้องอยู่รอดผ่านการสร้างใหม่

ไฟล์ owned ไม่ถูกทำ fingerprint เด็ดขาด — นั่นแหละคือ (IS) คำสัญญา อย่าใส่โค้ดของคุณ ไว้ในไฟล์ generated “แค่ชั่วคราว”: การสร้างใหม่ครั้งถัดไปจะปฏิเสธการรันทั้งหมดโดยระบุ ชื่อไฟล์นั้น

ในไซต์ จุดต่อขยายที่มี type อยู่ใน apps/site/src/owned/: Head.astro, BodyEnd.astro, slot ของ layout, section Wrapper/overrides และ config.mjs สำหรับ Astro integration และ origin ของ site

แก้ไข package.json และติดตั้งผ่านตัวติดตั้งที่ผ่านการตรวจสอบ:

Terminal window
node scripts/install-dependencies.mjs

รายการของคุณจะถูกเก็บไว้ — ดู “การ merge manifest” npm ci และ npm install ไม่ใช่ เส้นทางการติดตั้ง: ตัวติดตั้งจะตรวจสอบ npm เทียบกับ .nvmrc และ packageManager, แตกไฟล์โดยปิด script, audit tree เทียบกับ allowScripts ที่แน่นอน จากนั้น รัน npm rebuild สำหรับ script ที่อนุมัติแล้วเท่านั้น

dependency ของไซต์ใส่ไว้ใน apps/site/package.json และติดตั้งด้วย node scripts/install-dependencies.mjs apps/site island ของ Svelte หรือ React ยัง ต้องเพิ่ม integration ที่ปักหมุดเวอร์ชันไว้ลงใน apps/site/src/owned/config.mjs ด้วย

แก้ไข wrangler.jsonc ไฟล์นี้เป็น shared: เขียนเมื่อยังไม่มี และไม่เขียนทับเด็ดขาด ดังนั้น binding ของคุณจะคงอยู่ wrangler.example.jsonc ที่อยู่ข้างกันเป็น GENERATED — เป็น template ที่ไม่มี account id, ไม่มี route และไม่มี database id เพราะ generator นี้ไม่ได้ ตัดสินใจว่าคุณจะ deploy ที่ใด ให้คัดลอกจากไฟล์นั้น อย่าแก้ไขมัน

wrangler.build.jsonc ก็เป็นไฟล์ generated เช่นกัน และเป็นสิ่งที่ npm run build ใช้ bundle wrangler.deploy.jsonc ถูกเขียนโดย deployment (npm run deploy:configure) และอยู่ใน gitignore

binding ของไซต์ใส่ไว้ใน apps/site/wrangler.jsonc ซึ่งเป็น shared ภายใต้เงื่อนไขเดียวกัน

เพิ่ม workflow ภายใต้ .github/workflows/ โดยใช้ชื่อใดก็ได้ยกเว้น dee-wan-build.yml และ deploy-from-repository.yml .github/ เป็น prefix แบบ shared ดังนั้นไฟล์ของคุณจะถูกเขียนโดยคุณและไม่ถูกแตะต้องเลย สองชื่อนั้นเป็น generated: เป็นการกำหนดค่าของเครื่อง และการแก้ไขไฟล์เหล่านี้ต้องไปถึงโปรเจกต์ของคุณ ในการสร้างใหม่ครั้งถัดไป

การสร้างใหม่เขียนทุก path ที่เป็น generated ใหม่จากนิยามโมเดลปัจจุบัน: schema, route, worker entry, build script, workflow ที่ถูกส่งออก, ไซต์ที่ถูกส่งออก มัน เพิ่ม migration ต่อท้ายสำหรับการเปลี่ยน schema — ไม่เขียน migration ในอดีตใหม่เด็ดขาด

มันไม่แตะต้อง (NOT):

  • สิ่งใดภายใต้ src/owned/, apps/site/src/owned/ หรือ apps/site/src/pages/owned/,
  • ไฟล์ shared ที่มีอยู่แล้ว (wrangler.jsonc ยังคง binding ของคุณไว้),
  • migration ที่ถูก apply แล้ว,
  • ประวัติ git ของคุณ

package.json เป็นไฟล์ shared ไฟล์เดียวที่มันเปลี่ยนแปลง (DOES change) และเปลี่ยนด้วยการเพิ่ม (ADDING) เท่านั้น ดู “การ merge manifest”

ตัวเลือก guard ของ route ที่สร้างขึ้นมาจาก Settings → Source & delivery → Generated API guards การสร้างใหม่จะเขียนค่าเหล่านี้ลงใน route config ที่สร้างขึ้น ตัวเลือกที่ไม่ได้ตั้งค่าจะใช้ค่าเริ่มต้นของ package

Settings, Source & delivery, Generated API guards: แต่ละตัวเลือกพร้อมค่าเริ่มต้นและค่าแบบ hardened

sidecar .dee-wan-files.json เก็บ fingerprint ของไฟล์ generated ทุกไฟล์ตามที่ emitter เขียนไว้ครั้งล่าสุด นั่นคือวิธีตรวจจับว่า “คุณแก้ไขไฟล์นี้” ไฟล์ owned ไม่ถูกทำ fingerprint เด็ดขาด — นั่นแหละคือ (IS) คำสัญญา

การสร้างใหม่ทำงานได้โดยไม่ต้องมี CMS generator ถูกคอมไพล์ไว้ใน repository ของคุณ ตอน graduation (scripts/dee-wan-generator.mjs), ปักหมุดด้วย digest ใน graduation.generator.json และทุก script ที่ถูกส่งออกจะตรวจสอบหมุดนั้นก่อนที่จะ import มัน npm run regenerate ต่อ schema, app, Prisma client และการเขียนใหม่เพื่อการย้ายระบบ (portability) เข้าด้วยกันตามลำดับนั้น

ไฟล์ generated ที่ถูกแก้ไขจะไม่ถูกเขียนทับ และไม่ถูกรับเป็นของคุณ การรันจะหยุดและ ระบุชื่อไฟล์:

  • ไฟล์ generated ที่ไบต์เปลี่ยนไป — <path> is generated and has been edited. Move your change under src/owned/, then delete this file so it can be regenerated.”
  • ไฟล์ที่ path แบบ generated ซึ่ง manifest ก่อนหน้าไม่เคยบันทึกไว้ — <path> is not in the previous manifest. If it is yours, move it under src/owned/ and delete it from here.”

การย้ายงาน: คัดลอกการเปลี่ยนแปลงไปยังไฟล์ใหม่ภายใต้ src/owned/, import มัน จาก src/owned/routes.ts, ลบไฟล์ที่ถูกแก้ไข แล้วสร้างใหม่ emitter จะเขียนไฟล์ที่ถูกลบขึ้นใหม่จากนิยาม

ไฟล์ที่ emitter นำออกจะถูกลบก็ต่อเมื่อเป็นของเรา และ (AND) ไม่ถูกแก้ไขเท่านั้น สิ่งอื่นใดจะถูกปล่อยไว้ที่เดิม

แถวของเนื้อหาทำตามสัญญาเดียวกันในระดับที่ลึกลงไปอีกขั้น: ตารางเนื้อหาที่สร้างขึ้น เป็นของการรัน และแถวที่ถูกเขียนลงในตารางนั้นจะทำให้การรันครั้งถัดไปปฏิเสธด้วย observed_row_set_mismatch ตารางของคุณเองและแถวของคุณเองจะอยู่รอด — นั่นคือ จุดประสงค์ของ src/owned/ และ migration ของคุณเอง

snapshot ที่เผยแพร่แล้ว data/published-snapshot.json เป็น GENERATED หากแก้ไข การสร้างใหม่ครั้งถัดไปจะปฏิเสธมันโดยระบุชื่อ ตัว loader ก็ปฏิเสธเช่นกัน: ไซต์ผิด, artifact ผิด, ไบต์เปลี่ยน, การโหลดบางส่วน, การเล่นซ้ำที่ขัดแย้งกัน การโหลดไบต์เดียวกัน สองครั้งปลอดภัยและไม่เขียนสิ่งใด

หลังการแยกตัว (detach) จะไม่มีสิ่งใดสร้างสิ่งใดใหม่อีก ดังนั้นทุกไฟล์ — รวมถึงไฟล์ generated — เป็นของคุณที่จะแก้ไขได้ ก่อนการแยกตัว ไฟล์ generated ที่ถูกแก้ไขจะถูก ปฏิเสธโดยระบุ path ที่แน่นอนของมัน

ไฟล์ package.json ทั้งสองไฟล์ — ที่ root และ apps/site/ — เป็น shared แต่ถูก MERGE กฎ “ไม่เขียนทับเด็ดขาด” แบบบริสุทธิ์ยังหมายถึง “ไม่เพิ่มเด็ดขาด” ด้วย ดังนั้นในวันที่ emitter เริ่มเขียน โค้ดที่ import package ใหม่ โปรเจกต์ที่ถูกรับไปดูแลแล้วจะสร้างใหม่ออกมาเป็น ซอร์สที่ npm ci resolve ไม่ได้ ความล้มเหลวจะปรากฏขึ้นอีกสองขั้นตอนถัดไปในรูปของ tsc --noEmit หรือ astro check ที่ระบุชื่อโมดูล ไม่ได้ระบุชื่อ dependency ที่ขาดไป

กฎการ merge ซึ่งเหมือนกันสำหรับทั้งสองไฟล์:

  • block ที่ถูก merge: dependencies, devDependencies, overrides;
  • key ที่ไฟล์ของคุณมีอยู่แล้ว: เป็นของคุณ (YOURS) ค่าไม่ถูกแตะต้อง รวมถึงเวอร์ชันที่คุณ จงใจเปลี่ยนออกจากเวอร์ชันที่ปักหมุดไว้;
  • key ที่มีเฉพาะใน template: ถูกเพิ่ม (ADDED);
  • ไม่มีสิ่งใดถูกลบออกเลย;
  • packageManager ถูกเพิ่มเมื่อยังไม่มี และเป็นของคุณเมื่อมีอยู่แล้ว;
  • รายการ allowScripts ถูกเพิ่มเฉพาะในกรณีที่คุณยังไม่ได้ตัดสินใจเกี่ยวกับ package และเวอร์ชันนั้น — ค่า true และ false ของคุณยังคงอยู่;
  • การสร้างใหม่ที่เหมือนกันครั้งที่สองเปลี่ยนแปลงศูนย์ไบต์

package.json ที่ parse ไม่ได้จะถูกปล่อยไว้โดยไม่แตะต้อง การเขียนทับคือทิศทาง ที่ทำลายข้อมูล และ build จะปฏิเสธแทนคุณไม่ว่าในกรณีใด

lockfile ทำตาม manifest เมื่อการ merge เพิ่มรายการ package-lock.json ที่ติดมาจะไม่ได้อธิบาย manifest อีกต่อไป ดังนั้น emitter จะรีเฟรชมัน ด้วย npm install --package-lock-only ก่อนติดตั้ง lockfile และ manifest จึงไม่สามารถขัดแย้งกันได้ สิ่งนี้ได้รับการพิสูจน์โดย backend/test/graduate-site-build.test.ts (ต้องเปิดใช้เอง, DEEWAN_SITE_BUILD=1): site manifest ที่ถูกรับไปดูแลแล้วจากก่อนที่จะมี block devDependencies สามารถติดตั้ง, typecheck และ build ได้หลังการสร้างใหม่

ประตูหน้า Workflow อ่าน CMS หรือไม่? สร้างใหม่หรือไม่? deploy เองหรือไม่?
managed dee-wan-build.yml ใช่ — DEE_WAN_URL, build token, เวอร์ชันของโมเดล ใช่ แล้ว commit ไม่ (NO) — มันเรียกอีกตัวหนึ่ง
repository-only deploy-from-repository.yml ไม่ (NO) ไม่ ใช่

workflow แบบ managed มีสาม job: build ดึงนิยาม, สร้างใหม่, ตรวจสอบความถูกต้อง และ commit; deploy เรียก deploy-from-repository.yml (workflow_call) สำหรับ commit นั้นพอดีและรอ; report แจ้ง ผลลัพธ์ให้ Dee Wan ภายใต้ build attempt ที่เริ่มมัน มันไม่มีขั้นตอน migrate, ไม่มี wrangler deploy และไม่มี probe ของตัวเอง เคยมีสองสำเนาที่คลาดเคลื่อนกัน: มีเพียงตัว managed ที่เคยรันใน managed build และมีเพียงตัว repository ที่เคยโหลด snapshot ที่ commit ไว้ — ดังนั้น managed deploy จึงให้บริการฐานข้อมูลว่างเปล่าและ รายงานว่าสำเร็จ

เส้นทาง repository-only คือสิ่งที่อยู่รอดหลังการแยกตัว เป็นสิ่งที่ managed build ใช้ deploy ผ่าน และเป็นสิ่งที่ rollback ใช้ มันไม่อ่าน URL ของ Dee Wan ไม่ออก build token และ ไม่เรียก endpoint ของ CMS ใด ๆ มัน deploy commit ที่ได้รับมาตรงตามนั้น และบันทึก commit ที่ deploy จริง มันส่ง git_sha, d1_uuid และ worker_name กลับไปให้ผู้เรียก

สามไฟล์ หากขาดไฟล์ใดไฟล์หนึ่ง repository จะ build ได้แต่ไม่ให้บริการ สิ่งใดเลย

ไฟล์ โซน สิ่งที่ไฟล์มี
package-lock.json shared tree ที่ resolve แล้วพร้อม integrity scripts/install-dependencies.mjs ต้องใช้ไฟล์นี้
data/published-snapshot.json generated แถวที่เผยแพร่แล้ว ผูกกับไซต์ + artifact + manifest + content digest
.github/workflows/deploy-from-repository.yml generated checkout commit ที่ระบุตรงตัว, ติดตั้งแบบตรวจสอบ, build, resolve D1, migrate, โหลด snapshot, deploy, ตรวจสอบ

การติดตั้งแบบตรวจสอบหมายถึง node scripts/install-dependencies.mjs [root] npm ที่มาพร้อมกับ Node เวอร์ชันใน .nvmrc ต้องตรงกับ packageManager npm ci แตกไฟล์โดยปิด script, tree ที่ติดตั้งแล้วถูก audit เทียบกับ allowScripts ที่แน่นอน จากนั้น npm rebuild จะรันเฉพาะ script ที่อนุมัติแล้ว .nvmrc เป็น shared: เขียนเมื่อยังไม่มี และไม่เขียนใหม่เด็ดขาด

lockfile เป็น SHARED ด้วยเหตุผลเดียวกับ package.json: adopter เพิ่ม dependency ของตัวเอง และ emitter ต้องไม่ทิ้งสิ่งเหล่านั้น emitter ยังคงรีเฟรชมันในทุกการรันด้วยการรัน npm install --package-lock-only บน package.json ที่ merge แล้ว ดังนั้น lockfile และ manifest จึงไม่สามารถขัดแย้งกันได้

โหลด snapshot ลงในฐานข้อมูลด้วย:

Terminal window
npm run db:init -- --database ./graduated.db # local SQLite, migration first
npm run data:load -- --d1 DB --config wrangler.deploy.jsonc # adopter D1

ทุกคำสั่งด้านล่างเป็น script ที่โปรเจกต์ที่ถูกส่งออกมีอยู่แล้ว

1. ติดตั้งและเตรียมฐานข้อมูลที่ใช้งานได้

Terminal window
node scripts/install-dependencies.mjs
npm run db:init -- --database ./graduated.db

2. เพิ่ม route แก้ไข src/owned/routes.ts — เพิ่ม routes.post('/checkout', …) ตามข้างต้น ไม่มีอะไรอื่นต้องเชื่อมต่ออีก: src/app.ts mount ownedRoutes ไว้ภายใต้ /api อยู่แล้ว

3. เพิ่ม dependency เพิ่มมันลงใน dependencies ใน package.json จากนั้น:

Terminal window
node scripts/install-dependencies.mjs

4. พิสูจน์ในเครื่อง

Terminal window
npm run typecheck
npm run build:serve
DATABASE_PATH=./graduated.db SITE_ID=<your site id> \
ACCESS_CONFIG_FILE=./access.local.json PORT=8787 npm run serve:local

จากนั้นใน shell อีกตัวหนึ่ง:

Terminal window
curl -i -X POST -H 'content-type: application/json' -d '{}' \
http://127.0.0.1:8787/api/checkout

digest สำหรับ access.local.json ได้มาจาก npm run credential:digest ซึ่ง อ่าน secret จาก stdin เพื่อไม่ให้ไปถึงประวัติของ shell หรือ ps เด็ดขาด

5. สร้างใหม่ ใช้ลำดับเดียวกับที่ managed build ใช้:

Terminal window
npm run regenerate # schema, app, security, local, manifest, Prisma client, portability
npm run migrate:emit # the next migration, append-only
npm run typecheck
node scripts/install-dependencies.mjs apps/site
npm run --prefix apps/site check
npm run --prefix apps/site build
npm run files:manifest # LAST: the ownership sidecar, from the tree that exists

route และ dependency ของคุณยังอยู่ที่เดิม git diff แสดงเฉพาะไฟล์ generated และ migration ที่ถูกเพิ่มต่อท้าย

6. Bundle และ deploy

Terminal window
npm run build # the Worker bundle, via wrangler.build.jsonc

การ deploy คือ workflow ไม่ใช่ script ในเครื่อง: commit จากนั้นรัน .github/workflows/deploy-from-repository.yml สำหรับ commit นั้นพอดีด้วย credential ของ Cloudflare ของคุณเอง มันติดตั้ง, build, resolve หรือสร้าง D1, migrate, โหลด snapshot, deploy และตรวจสอบ Rollback คือ workflow เดียวกัน กับ sha ที่เก่ากว่า npm run dev รัน Worker ในเครื่องโดยใช้ wrangler.jsonc หากคุณต้องการ wrangler dev แทน

หลังการ deploy npm run verify:deployment จะถาม origin ที่ deploy แล้วว่า ให้บริการเนื้อหาของ commit นี้ตรงทุกแถวหรือไม่ และ npm run verify:media จะถามว่า projection ที่ commit ไว้ยังชี้ไปที่ Dee Wan หรือไม่

พิสูจน์โดย backend/test/graduate-adoption.test.ts — “keeps a developer’s own file across a schema change”, “keeps rows the adopter wrote into their own table across a schema change”, “regenerates after a schema change, appending to the migration lineage”:

  1. สร้างและ deploy,
  2. แก้ไข src/owned/ และเพิ่มแถวลงในฐานข้อมูล,
  3. เพิ่มฟิลด์ให้โมเดลใน CMS,
  4. สร้างใหม่

หลังขั้นตอนที่ 4: ไฟล์ owned เหมือนเดิมทุกไบต์, มี migration ใหม่ถูกเพิ่มต่อท้าย, migration เก่าไม่ถูกแตะต้อง และแถวที่มีอยู่ยังคงอยู่