mulmoterminal 6.3.0 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blueprints/firebase/checks/deploy-check.sh +18 -0
- package/blueprints/firebase/checks/emulator-test.sh +11 -0
- package/blueprints/firebase/checks/google-signin.sh +14 -0
- package/blueprints/firebase/checks/java21.sh +28 -0
- package/blueprints/firebase/checks/page-renders.sh +28 -0
- package/blueprints/firebase/checks/prerequisites.sh +18 -0
- package/blueprints/firebase/checks/project-id.sh +9 -0
- package/blueprints/firebase/checks/projects.sh +12 -0
- package/blueprints/firebase/checks/protect.sh +30 -0
- package/blueprints/firebase/checks/scaffold.sh +12 -0
- package/blueprints/firebase/checks/spec.sh +8 -0
- package/blueprints/firebase/guides/app-check.md +13 -0
- package/blueprints/firebase/guides/billing.md +13 -0
- package/blueprints/firebase/guides/google-signin.md +14 -0
- package/blueprints/firebase/guides/sign-in.md +14 -0
- package/blueprints/firebase/guides/try-it.md +6 -0
- package/blueprints/firebase/infra/firebase.json +59 -0
- package/blueprints/firebase/infra/firestore.indexes.json +4 -0
- package/blueprints/firebase/infra/firestore.rules +9 -0
- package/blueprints/firebase/manifest.json +39 -0
- package/blueprints/firebase/plan.json +71 -0
- package/blueprints/firebase/security/checklist.md +11 -0
- package/blueprints/firebase/skills/auth/SKILL.md +22 -0
- package/blueprints/firebase/skills/data-rules/SKILL.md +28 -0
- package/blueprints/firebase/skills/deploy-dev/SKILL.md +25 -0
- package/blueprints/firebase/skills/deploy-production/SKILL.md +24 -0
- package/blueprints/firebase/skills/prerequisites/SKILL.md +29 -0
- package/blueprints/firebase/skills/projects/SKILL.md +35 -0
- package/blueprints/firebase/skills/protect/SKILL.md +23 -0
- package/blueprints/firebase/skills/scaffold/SKILL.md +33 -0
- package/blueprints/firebase/skills/spec/SKILL.md +30 -0
- package/blueprints/firebase/spec/overview.md +34 -0
- package/blueprints/internal/checks/auth-config.sh +7 -0
- package/blueprints/internal/checks/blocking-registered.sh +12 -0
- package/blueprints/internal/checks/identity-platform.sh +11 -0
- package/blueprints/internal/guides/blocking-functions.md +13 -0
- package/blueprints/internal/guides/identity-platform.md +11 -0
- package/blueprints/internal/guides/try-refusal.md +15 -0
- package/blueprints/internal/hearing.json +112 -0
- package/blueprints/internal/infra/firestore.rules +38 -0
- package/blueprints/internal/manifest.json +8 -0
- package/blueprints/internal/presets.json +26 -0
- package/blueprints/internal/security/threats.md +9 -0
- package/blueprints/internal/skills/audit-log/SKILL.md +23 -0
- package/blueprints/internal/skills/domain-restriction/SKILL.md +44 -0
- package/blueprints/internal/skills/offboarding/SKILL.md +27 -0
- package/blueprints/internal/skills/production-auth/SKILL.md +26 -0
- package/blueprints/internal/skills/roles/SKILL.md +26 -0
- package/blueprints/internal/skills/verify-dev/SKILL.md +22 -0
- package/blueprints/internal/spec/requirements.md +32 -0
- package/blueprints/internal/steps.json +53 -0
- package/blueprints/local/checks/handover.sh +6 -0
- package/blueprints/local/checks/node-sqlite.sh +6 -0
- package/blueprints/local/checks/scaffold.sh +12 -0
- package/blueprints/local/checks/smoke.sh +34 -0
- package/blueprints/local/checks/spec.sh +8 -0
- package/blueprints/local/checks/tests-pass.sh +7 -0
- package/blueprints/local/manifest.json +21 -0
- package/blueprints/local/plan.json +68 -0
- package/blueprints/local/security/checklist.md +9 -0
- package/blueprints/local/skills/api/SKILL.md +22 -0
- package/blueprints/local/skills/auth/SKILL.md +27 -0
- package/blueprints/local/skills/data/SKILL.md +22 -0
- package/blueprints/local/skills/handover/SKILL.md +23 -0
- package/blueprints/local/skills/prerequisites/SKILL.md +22 -0
- package/blueprints/local/skills/run-check/SKILL.md +19 -0
- package/blueprints/local/skills/scaffold/SKILL.md +35 -0
- package/blueprints/local/skills/spec/SKILL.md +29 -0
- package/blueprints/local/skills/ui/SKILL.md +22 -0
- package/blueprints/local/spec/overview.md +33 -0
- package/blueprints/product/hearing.json +92 -0
- package/blueprints/product/manifest.json +8 -0
- package/blueprints/product/presets.json +101 -0
- package/blueprints/product/security/threats.md +15 -0
- package/blueprints/product/skills/acceptance/SKILL.md +26 -0
- package/blueprints/product/skills/acceptance-firebase/SKILL.md +27 -0
- package/blueprints/product/skills/features-firebase/SKILL.md +28 -0
- package/blueprints/product/spec/requirements.md +42 -0
- package/blueprints/product/steps.json +30 -0
- package/blueprints/refactor/checks/prs.sh +22 -0
- package/blueprints/refactor/checks/report.sh +6 -0
- package/blueprints/refactor/checks/targets.mjs +114 -0
- package/blueprints/refactor/checks/tranche.sh +10 -0
- package/blueprints/refactor/hearing.json +44 -0
- package/blueprints/refactor/manifest.json +8 -0
- package/blueprints/refactor/skills/report/SKILL.md +26 -0
- package/blueprints/refactor/skills/survey/SKILL.md +87 -0
- package/blueprints/refactor/skills/tranche/SKILL.md +172 -0
- package/blueprints/refactor/skills/tranche/cross-review.md +72 -0
- package/blueprints/refactor/steps.json +27 -0
- package/blueprints/repo/checks/ci-check.sh +36 -0
- package/blueprints/repo/checks/ci-recorded.sh +10 -0
- package/blueprints/repo/checks/default-branch.sh +4 -0
- package/blueprints/repo/checks/gates.sh +24 -0
- package/blueprints/repo/checks/repo-check.sh +14 -0
- package/blueprints/repo/guides/gh-login.md +12 -0
- package/blueprints/repo/guides/trust-repo.md +10 -0
- package/blueprints/repo/manifest.json +13 -0
- package/blueprints/repo/plan.json +25 -0
- package/blueprints/repo/skills/baseline/SKILL.md +42 -0
- package/blueprints/repo/skills/ci/SKILL.md +35 -0
- package/blueprints/repo/skills/state/SKILL.md +32 -0
- package/common/blueprint/executorPolicy.ts +56 -0
- package/common/blueprint/hearing.ts +98 -0
- package/common/blueprint/manifest.ts +48 -0
- package/common/blueprint/plan.ts +88 -0
- package/common/blueprint/presets.ts +24 -0
- package/common/blueprint/registry.ts +82 -0
- package/common/blueprint/relativePath.ts +7 -0
- package/common/blueprint/run.ts +70 -0
- package/common/blueprint/specRevisionPrompt.ts +36 -0
- package/common/blueprint/state.ts +187 -0
- package/common/blueprint/stepPrompt.ts +62 -0
- package/common/keymap.ts +113 -19
- package/common/markedUnread.ts +6 -0
- package/common/previewTheme.ts +39 -0
- package/common/themeVars.ts +7 -2
- package/dist/assets/{abnfDiagram-O67JEVCF-DxIs7k89.js → abnfDiagram-O67JEVCF-BMXgccj_.js} +1 -1
- package/dist/assets/architecture-7GRP2DOG-DDa1-16D.js +1 -0
- package/dist/assets/{architectureDiagram-NJMV4G6O-nEIJ1IPC.js → architectureDiagram-NJMV4G6O-Gufkh-vj.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-E59vkErT.js → blockDiagram-BEXU5L5S-q-o3wn2P.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-JwRKcRsT.js → c4Diagram-YGBWAQC7-CL-JfteH.js} +1 -1
- package/dist/assets/channel-DyPaG4wc.js +1 -0
- package/dist/assets/{chunk-3FUC2YCW-CCLEOkDF.js → chunk-3FUC2YCW-BC3xA0w_.js} +1 -1
- package/dist/assets/{chunk-5DYCD2WN-1AgUN0r-.js → chunk-5DYCD2WN-D88b7JA7.js} +1 -1
- package/dist/assets/{chunk-742MDFTN-DSUTqyro.js → chunk-742MDFTN-CkTbF_y_.js} +1 -1
- package/dist/assets/{chunk-7INBJB4K-4ocpGodg.js → chunk-7INBJB4K-Dm5xErYY.js} +1 -1
- package/dist/assets/{chunk-7M6MHVWA-DhIWQ16X.js → chunk-7M6MHVWA-BKTgDHiW.js} +1 -1
- package/dist/assets/{chunk-7PRAP22T-CMK5Hvie.js → chunk-7PRAP22T-B1I8JlJD.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-Dww8IZlE.js → chunk-GTNCS2PH-CNw7mi1S.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-BOY8sjUW.js → chunk-GWA4HPMP-O4QADGkE.js} +1 -1
- package/dist/assets/{chunk-MBY4JIJT-CyPFVaTq.js → chunk-MBY4JIJT-DTVnnNc1.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-CYeZvn4f.js → chunk-NETBCI7D-DGYtuQck.js} +1 -1
- package/dist/assets/{chunk-O7XYJQB3-LuTg7vRC.js → chunk-O7XYJQB3-CoTDAkDD.js} +1 -1
- package/dist/assets/{chunk-UA2S7LBM-BeT6kQnw.js → chunk-UA2S7LBM-CBZ7E5ky.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-DPztbhwz.js → chunk-WEXAMYUT-3ijA0Lqe.js} +1 -1
- package/dist/assets/{chunk-Z7XXMR3K-D02R_oPs.js → chunk-Z7XXMR3K-BdwRk-Dj.js} +1 -1
- package/dist/assets/{chunk-ZIGJFQKS-CAGBrgq6.js → chunk-ZIGJFQKS-NABrV8Ma.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-cNPpvvCl.js → classDiagram-v2-NBCMYWYE-BWuCYNS4.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-4vqsfXcN.js → cynefin-OW5HDTMX-d2MfeLbK.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-BsaAwSEj.js → cynefinDiagram-VND7K2PF-CtTe0_eT.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-B5er0lNr.js → dagre-6A5THRUB-B62rmrkC.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-BP4RiqIl.js → diagram-22UHCM2B-lcFR-LdR.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-DDKPFcUe.js → diagram-3UASUU5V-DSxpBtA9.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-BPuCp6dP.js → diagram-ATOU4E4O-DF4bhjkZ.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-TIsGX923.js → diagram-CDSNMT55-BnmZ6gVC.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-DIIMtWIN.js → diagram-MLGK6HIB-BSwIKBMC.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-BlSps7Ge.js → diagram-MPIPVDR6-__24DBss.js} +1 -1
- package/dist/assets/{dist-VlPZWpqj.js → dist-B4ZkhYig.js} +1 -1
- package/dist/assets/{dist-1Wq3lKB9.js → dist-BSwD-BM8.js} +1 -1
- package/dist/assets/{dist-mXycgwze.js → dist-DfgKa3Md.js} +1 -1
- package/dist/assets/{dist-g1xDz2eh.js → dist-Dpq76YZY.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-CoQ9a6l2.js → ebnfDiagram-ZINNZB2B-Dyadybu9.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CQpQ3e0G.js → elk-276RUBZZ-DlmhGwgs.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-OnRfD1Na.js → erDiagram-OPXOYQCR-B4COZmjq.js} +1 -1
- package/dist/assets/eventmodeling-NTZA5JFV-o_Kwa1Ws.js +1 -0
- package/dist/assets/flowDiagram-KWPJA3E3-CB69C2wZ.js +1 -0
- package/dist/assets/{ganttDiagram-FUAMR5RP-DTtKLRRT.js → ganttDiagram-FUAMR5RP-DVzqgWpl.js} +1 -1
- package/dist/assets/{gitGraph-4MIJSDKK-qTac1fxV.js → gitGraph-4MIJSDKK-AkcCbjZe.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-DPIUDRK2.js → gitGraphDiagram-X574FWY7-CASoaHF0.js} +1 -1
- package/dist/assets/{index-B5tJhuzV.js → index-CkAy96Rk.js} +400 -390
- package/dist/assets/index-gkjZAr4W.css +1 -0
- package/dist/assets/{info-A6RAGUB7-oMNiXi0M.js → info-A6RAGUB7-WXoDczqb.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-B0NktbNw.js → infoDiagram-VRGFBTTK-DmcQ8wga.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-b4emOwCC.js → ishikawaDiagram-OU5B5YK6-D_oyvMTb.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-CJS8YQyv.js → journeyDiagram-ZHPQQLJL-Dd3r8OKF.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-ByUpoGJS.js → kanban-definition-PNTS6WVX-CiyfIr1D.js} +1 -1
- package/dist/assets/{line-8OW2LsyA.js → line-DKtrpJF_.js} +1 -1
- package/dist/assets/{mermaid-parser.core-B5FlVjCr.js → mermaid-parser.core-DF5ro2JP.js} +3 -3
- package/dist/assets/{mermaid.core--DeGxg9T.js → mermaid.core-GXmQCbpB.js} +4 -4
- package/dist/assets/{mindmap-definition-NLK3R4M7-BboAGlJq.js → mindmap-definition-NLK3R4M7-BhgUxsmy.js} +1 -1
- package/dist/assets/{packet-AYTQ26CC-Dx1cTPUp.js → packet-AYTQ26CC-SlHS35ji.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-BVLZicUF.js → pegDiagram-GJSIUBJH-b1cLRCef.js} +1 -1
- package/dist/assets/{pie-WAS4IAKB-BSxTLBfV.js → pie-WAS4IAKB-DSd1EToP.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-BfSPFVSo.js → pieDiagram-5QR66LMP-DnVpLbOF.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-BcEr3u-n.js → quadrantDiagram-O4NWA36T-B3O-aZ11.js} +1 -1
- package/dist/assets/{radar-RG4KPBEZ-6ha11zcy.js → radar-RG4KPBEZ-D2NFD4uV.js} +1 -1
- package/dist/assets/{railroad-74A4TZTK-CAfLdF7d.js → railroad-74A4TZTK-CCWG5FRX.js} +1 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-C9TIgVrQ.js +1 -0
- package/dist/assets/railroad-ebnf-LZEXJU2U-Ml9Kx7kC.js +1 -0
- package/dist/assets/railroad-peg-WCYAUIDC-yEHD5frx.js +1 -0
- package/dist/assets/{railroadDiagram-XR7U4H2S-CqOU7QD4.js → railroadDiagram-XR7U4H2S-Clx3eLUr.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-cd-45UL-.js → requirementDiagram-PLB6GJNP-V_dmyYjn.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-CRSXwjHr.js → sankeyDiagram-IPEJSGJF-DsZTTMgR.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-Bd9pHNff.js → sequenceDiagram-PO4LG4MO-CTdA-nU3.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-BHX-09AB.js → stateDiagram-v2-GCMORJYK-BMW-yY3-.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-Wa3AByx7.js → swimlanes-2SLR337P-XiK4wd-W.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-CRegG93P.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-CrTeIW7f.js → timeline-definition-EJHVYXUP-D9_w4vph.js} +1 -1
- package/dist/assets/{treeView-Q6P3EWNA-s8edkU8_.js → treeView-Q6P3EWNA-qoNv5B06.js} +1 -1
- package/dist/assets/{treemap-WGGIJYW6-CAOvhdFB.js → treemap-WGGIJYW6-CK1HxgjP.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-D58JgkZs.js → usecaseDiagram-POWQR4AR-D2Sm7SiY.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-Cx5jv6Dl.js → vennDiagram-UO4OBE2U-2_2NY98P.js} +1 -1
- package/dist/assets/{wardley-WFR3VGLG-DRCvCrcV.js → wardley-WFR3VGLG-CYOan3iJ.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-qidEa5K9.js → wardleyDiagram-VNRHLVJA-DBCTgg6V.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-DbAy_Psm.js → xychartDiagram-PMCCYNJV-DP1TMWiQ.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +3 -2
- package/server/blueprint/answersFile.ts +11 -0
- package/server/blueprint/checkRunner.ts +78 -0
- package/server/blueprint/executor.ts +397 -0
- package/server/blueprint/executorLock.ts +96 -0
- package/server/blueprint/installer.ts +142 -0
- package/server/blueprint/marketRoutes.ts +104 -0
- package/server/blueprint/packs.ts +134 -0
- package/server/blueprint/registry.ts +68 -0
- package/server/blueprint/routes.ts +195 -0
- package/server/blueprint/runStore.ts +49 -0
- package/server/blueprint/trust.ts +67 -0
- package/server/blueprint/wiring.ts +172 -0
- package/server/config/keymap-check.ts +1 -1
- package/server/files/files-browse.ts +8 -5
- package/server/files/renderedDoc.ts +16 -1
- package/server/git/spawn-collect.ts +11 -2
- package/server/index.ts +1 -1
- package/server/routes/app-routes.ts +3 -0
- package/server/session/pty-connection.ts +31 -12
- package/server/skills/mulmoterminal-keys/SKILL.md +28 -4
- package/dist/assets/architecture-7GRP2DOG-pJx6zX85.js +0 -1
- package/dist/assets/channel-C3MrC-mz.js +0 -1
- package/dist/assets/eventmodeling-NTZA5JFV-rG0naOXp.js +0 -1
- package/dist/assets/flowDiagram-KWPJA3E3-CRzZTuXz.js +0 -1
- package/dist/assets/index-C34PsKMj.css +0 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-BuNXWChV.js +0 -1
- package/dist/assets/railroad-ebnf-LZEXJU2U-ILhcFgfq.js +0 -1
- package/dist/assets/railroad-peg-WCYAUIDC-i86jT9nY.js +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BNVwSwZd.js +0 -8
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# The Hosting site of the given alias (dev / prod) serves the build this deploy made: the deploy
|
|
3
|
+
# writes a fresh id to .blueprint/build-id and ships the same id as /blueprint-build.txt. No git is
|
|
4
|
+
# needed — and none is wanted: a new repository takes away the folder's trust, and the next
|
|
5
|
+
# unattended step would stop at Claude Code's trust prompt.
|
|
6
|
+
set -eu
|
|
7
|
+
id=$(sh "$(dirname "$0")/project-id.sh" "$1")
|
|
8
|
+
[ -s .blueprint/build-id ] || { echo "missing .blueprint/build-id; the deploy step writes it before deploying" >&2; exit 1; }
|
|
9
|
+
expected=$(cat .blueprint/build-id)
|
|
10
|
+
served=$(curl -fsS --max-time 20 "https://$id.web.app/blueprint-build.txt")
|
|
11
|
+
[ "$served" = "$expected" ] || { echo "$id serves build $served, this deploy made $expected" >&2; exit 1; }
|
|
12
|
+
# Firebase Hosting hands the app its config at this reserved URL; the app reads it rather than
|
|
13
|
+
# carrying a key of its own, so the same build works on dev and prod.
|
|
14
|
+
curl -fsS --max-time 20 "https://$id.web.app/__/firebase/init.json" | node -e 'let s="";process.stdin.on("data",(c)=>s+=c).on("end",()=>{
|
|
15
|
+
if (!JSON.parse(s).apiKey) { console.error("no web app config at /__/firebase/init.json; register a web app in the project"); process.exit(1); }
|
|
16
|
+
})'
|
|
17
|
+
sh "$(dirname "$0")/google-signin.sh" "$1"
|
|
18
|
+
sh "$(dirname "$0")/page-renders.sh" "https://$id.web.app/"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Runs test/blueprint/<name>.spec.ts against the emulators, under a demo- project id so it can never
|
|
3
|
+
# reach a real project.
|
|
4
|
+
set -eu
|
|
5
|
+
. "$(dirname "$0")/java21.sh"
|
|
6
|
+
name="$1"
|
|
7
|
+
spec="test/blueprint/$name.spec.ts"
|
|
8
|
+
[ -f "$spec" ] || { echo "missing $spec" >&2; exit 1; }
|
|
9
|
+
only="auth,firestore"
|
|
10
|
+
[ -d functions ] && only="$only,functions"
|
|
11
|
+
firebase emulators:exec --project demo-blueprint --only "$only" "npx vitest run $spec"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Google sign-in is switched on in the real project behind an alias (dev / prod). The emulator accepts
|
|
3
|
+
# any provider, so only this shows whether a person can actually sign in; switching it on creates an
|
|
4
|
+
# OAuth client, which only the console does, so a person has to click it.
|
|
5
|
+
set -eu
|
|
6
|
+
id=$(sh "$(dirname "$0")/project-id.sh" "$1")
|
|
7
|
+
token=$(gcloud auth print-access-token)
|
|
8
|
+
config=$(curl -sS --max-time 20 -H "Authorization: Bearer $token" -H "X-Goog-User-Project: $id" \
|
|
9
|
+
"https://identitytoolkit.googleapis.com/admin/v2/projects/$id/defaultSupportedIdpConfigs/google.com")
|
|
10
|
+
printf '%s' "$config" | node -e 'let s="";process.stdin.on("data",(c)=>s+=c).on("end",()=>{
|
|
11
|
+
if (JSON.parse(s).enabled === true) return;
|
|
12
|
+
console.error("Google sign-in is not enabled; turn it on at https://console.firebase.google.com/project/" + process.argv[1] + "/authentication/providers");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
})' "$id"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Sourced by the checks that start the Firebase emulators, which need a JDK 21 or later. A brew
|
|
3
|
+
# `openjdk@21` is keg-only and not on PATH, so look in the usual places and put the first new enough
|
|
4
|
+
# one in front, rather than failing on whichever `java` happens to be first.
|
|
5
|
+
java_major() {
|
|
6
|
+
"$1" -version 2>&1 | sed -n 's/.*version "\([0-9][0-9]*\).*/\1/p' | head -1
|
|
7
|
+
}
|
|
8
|
+
blueprint_java_found=""
|
|
9
|
+
for candidate in \
|
|
10
|
+
"${JAVA_HOME:-/nonexistent}/bin/java" \
|
|
11
|
+
"$(/usr/libexec/java_home -v 21+ 2>/dev/null || echo /nonexistent)/bin/java" \
|
|
12
|
+
/opt/homebrew/opt/openjdk@21/bin/java \
|
|
13
|
+
/usr/local/opt/openjdk@21/bin/java \
|
|
14
|
+
/opt/homebrew/opt/openjdk/bin/java \
|
|
15
|
+
"$(command -v java || echo /nonexistent)"; do
|
|
16
|
+
[ -x "$candidate" ] || continue
|
|
17
|
+
major=$(java_major "$candidate")
|
|
18
|
+
if [ "${major:-0}" -ge 21 ]; then
|
|
19
|
+
blueprint_java_found="$candidate"
|
|
20
|
+
break
|
|
21
|
+
fi
|
|
22
|
+
done
|
|
23
|
+
if [ -z "$blueprint_java_found" ]; then
|
|
24
|
+
echo "no JDK 21 or later found; the Firebase emulators need one (macOS: brew install openjdk@21)" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
PATH="$(dirname "$blueprint_java_found"):${PATH:-}"
|
|
28
|
+
export PATH
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# The served page actually starts: a build can deploy, report its id and answer 200, and still be a
|
|
3
|
+
# blank page because the app threw on load (a missing config does exactly that). Rendered in headless
|
|
4
|
+
# Chrome, the page must show some text. Without a Chrome this cannot be judged, and says so.
|
|
5
|
+
set -eu
|
|
6
|
+
url="$1"
|
|
7
|
+
chrome=""
|
|
8
|
+
for candidate in "${CHROME:-}" "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" google-chrome chromium chromium-browser; do
|
|
9
|
+
[ -n "$candidate" ] || continue
|
|
10
|
+
if [ -x "$candidate" ] || command -v "$candidate" >/dev/null 2>&1; then chrome="$candidate"; break; fi
|
|
11
|
+
done
|
|
12
|
+
[ -n "$chrome" ] || { echo "no Chrome found (set CHROME=...); could not confirm that $url renders" >&2; exit 0; }
|
|
13
|
+
# Headless Chrome can print the DOM and then not exit, so it is waited on for the DOM, not for exit.
|
|
14
|
+
RENDER_LIMIT_SECONDS=60
|
|
15
|
+
work=$(mktemp -d)
|
|
16
|
+
"$chrome" --headless=new --disable-gpu --no-first-run --user-data-dir="$work/profile" --virtual-time-budget=15000 --dump-dom "$url" >"$work/dom.html" 2>/dev/null &
|
|
17
|
+
pid=$!
|
|
18
|
+
trap 'kill "$pid" 2>/dev/null || true; rm -rf "$work"' EXIT
|
|
19
|
+
waited=0
|
|
20
|
+
until grep -q "</html>" "$work/dom.html" 2>/dev/null || [ "$waited" -ge "$RENDER_LIMIT_SECONDS" ] || ! kill -0 "$pid" 2>/dev/null; do
|
|
21
|
+
sleep 1
|
|
22
|
+
waited=$((waited + 1))
|
|
23
|
+
done
|
|
24
|
+
node -e 'let s="";process.stdin.on("data",(c)=>s+=c).on("end",()=>{
|
|
25
|
+
const body = (s.match(/<body[^>]*>([\s\S]*)<\/body>/i) || [, ""])[1];
|
|
26
|
+
const text = body.replace(/<(script|style|noscript)[\s\S]*?<\/\1>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
27
|
+
if (!text) { console.error(process.argv[1] + " renders a blank page: the app failed to start. Open it in a browser and read the console error."); process.exit(1); }
|
|
28
|
+
})' "$url" <"$work/dom.html"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Every required CLI answers, a JDK new enough for the emulators exists, and Firebase and gcloud are
|
|
3
|
+
# signed in to the SAME account — the checks use both, and a project one of them created is not
|
|
4
|
+
# necessarily visible to the other.
|
|
5
|
+
set -eu
|
|
6
|
+
for probe in "node --version" "yarn --version" "firebase --version" "gcloud --version"; do
|
|
7
|
+
sh -c "$probe" >/dev/null 2>&1 || { echo "missing: $probe" >&2; exit 1; }
|
|
8
|
+
done
|
|
9
|
+
. "$(dirname "$0")/java21.sh"
|
|
10
|
+
firebase_account=$(firebase login:list 2>/dev/null | sed -n 's/.*Logged in as \([^ ]*\).*/\1/p' | head -1)
|
|
11
|
+
[ -n "$firebase_account" ] || { echo "firebase: not signed in (firebase login)" >&2; exit 1; }
|
|
12
|
+
gcloud_account=$(gcloud auth list --filter=status:ACTIVE --format='value(account)' 2>/dev/null || true)
|
|
13
|
+
[ -n "$gcloud_account" ] || { echo "gcloud: not signed in (gcloud auth login)" >&2; exit 1; }
|
|
14
|
+
[ "$firebase_account" = "$gcloud_account" ] || {
|
|
15
|
+
echo "firebase is signed in as $firebase_account but gcloud as $gcloud_account; use one account for both (gcloud config set account $firebase_account)" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
}
|
|
18
|
+
gcloud projects list --limit=1 --format='value(projectId)' >/dev/null 2>&1 || { echo "gcloud: the sign-in has expired (gcloud auth login $gcloud_account)" >&2; exit 1; }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Prints the project id behind a .firebaserc alias (dev / prod).
|
|
3
|
+
set -eu
|
|
4
|
+
node -e '
|
|
5
|
+
const rc = JSON.parse(require("fs").readFileSync(".firebaserc", "utf8"));
|
|
6
|
+
const id = rc.projects && rc.projects[process.argv[1]];
|
|
7
|
+
if (typeof id !== "string" || id === "") { console.error("no project for alias " + process.argv[1]); process.exit(1); }
|
|
8
|
+
console.log(id);
|
|
9
|
+
' "$1"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# .firebaserc names two different projects; each exists, has billing on (Blaze) and has Firestore.
|
|
3
|
+
set -eu
|
|
4
|
+
here=$(dirname "$0")
|
|
5
|
+
dev=$(sh "$here/project-id.sh" dev)
|
|
6
|
+
prod=$(sh "$here/project-id.sh" prod)
|
|
7
|
+
[ "$dev" != "$prod" ] || { echo "dev and prod are the same project: $dev" >&2; exit 1; }
|
|
8
|
+
for id in "$dev" "$prod"; do
|
|
9
|
+
billing=$(gcloud billing projects describe "$id" --format='value(billingEnabled)')
|
|
10
|
+
[ "$billing" = "True" ] || { echo "$id: billing is not enabled (Blaze plan)" >&2; exit 1; }
|
|
11
|
+
gcloud firestore databases describe --project "$id" --database='(default)' >/dev/null || { echo "$id: no Firestore database" >&2; exit 1; }
|
|
12
|
+
done
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# App Check is ENFORCED for Firestore on production, every callable function enforces it, and a
|
|
3
|
+
# budget is scoped to the production project. Each response is captured before it is parsed, so a
|
|
4
|
+
# failed request fails the check instead of hiding behind a pipe.
|
|
5
|
+
set -eu
|
|
6
|
+
prod=$(sh "$(dirname "$0")/project-id.sh" prod)
|
|
7
|
+
number=$(gcloud projects describe "$prod" --format='value(projectNumber)')
|
|
8
|
+
token=$(gcloud auth print-access-token)
|
|
9
|
+
|
|
10
|
+
appcheck=$(curl -fsS --max-time 20 -H "Authorization: Bearer $token" \
|
|
11
|
+
"https://firebaseappcheck.googleapis.com/v1/projects/$number/services/firestore.googleapis.com")
|
|
12
|
+
printf '%s' "$appcheck" | node -e 'let s="";process.stdin.on("data",(c)=>s+=c).on("end",()=>{
|
|
13
|
+
const mode = JSON.parse(s).enforcementMode;
|
|
14
|
+
if (mode !== "ENFORCED") { console.error("App Check for Firestore is " + mode + ", not ENFORCED"); process.exit(1); }
|
|
15
|
+
})'
|
|
16
|
+
|
|
17
|
+
# Line by line, so one protected callable cannot vouch for another.
|
|
18
|
+
unprotected=$(grep -rn "onCall(" functions/src | grep -v "onCall({ *enforceAppCheck: true" || true)
|
|
19
|
+
[ -z "$unprotected" ] || { printf 'callable functions without enforceAppCheck:\n%s\n' "$unprotected" >&2; exit 1; }
|
|
20
|
+
|
|
21
|
+
account=$(gcloud billing projects describe "$prod" --format='value(billingAccountName)')
|
|
22
|
+
account=${account#billingAccounts/}
|
|
23
|
+
[ -n "$account" ] || { echo "$prod has no billing account" >&2; exit 1; }
|
|
24
|
+
budgets=$(gcloud billing budgets list --billing-account="$account" --format=json)
|
|
25
|
+
# gcloud records the filter as projects/<id>; the API also accepts projects/<number>.
|
|
26
|
+
printf '%s' "$budgets" | node -e 'let s="";process.stdin.on("data",(c)=>s+=c).on("end",()=>{
|
|
27
|
+
const wanted = process.argv.slice(1).map((p) => "projects/" + p);
|
|
28
|
+
const scoped = JSON.parse(s).some((b) => (b.budgetFilter?.projects ?? []).some((p) => wanted.includes(p)));
|
|
29
|
+
if (!scoped) { console.error("no budget is scoped to " + wanted.join(" or ")); process.exit(1); }
|
|
30
|
+
})' "$prod" "$number"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# The skeleton exists, carries the emulator and test harness later steps rely on, and builds.
|
|
3
|
+
set -eu
|
|
4
|
+
for f in firebase.json package.json firestore.rules; do
|
|
5
|
+
[ -f "$f" ] || { echo "missing $f" >&2; exit 1; }
|
|
6
|
+
done
|
|
7
|
+
for d in functions test/blueprint; do
|
|
8
|
+
[ -d "$d" ] || { echo "missing $d/" >&2; exit 1; }
|
|
9
|
+
done
|
|
10
|
+
grep -q '"emulators"' firebase.json || { echo "firebase.json configures no emulators" >&2; exit 1; }
|
|
11
|
+
grep -q '"@firebase/rules-unit-testing"' package.json || { echo "@firebase/rules-unit-testing is not a dependency" >&2; exit 1; }
|
|
12
|
+
yarn build
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# The spec exists and every template placeholder in it was filled or marked undecided.
|
|
3
|
+
set -eu
|
|
4
|
+
[ -s .blueprint/spec.md ] || { echo "missing .blueprint/spec.md" >&2; exit 1; }
|
|
5
|
+
if grep -n '{{' .blueprint/spec.md >&2; then
|
|
6
|
+
echo "unfilled placeholders are left in .blueprint/spec.md" >&2
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# App Check の適用を有効にする
|
|
2
|
+
|
|
3
|
+
アプリ以外からデータベースや関数を呼ばれないようにする仕組みです。有効にすると、このアプリ以外からの呼び出しは断られます。
|
|
4
|
+
|
|
5
|
+
1. 次のページを開きます: https://console.firebase.google.com/project/{{PROJECT_ID}}/appcheck/products
|
|
6
|
+
2. 「API」(APIs)の一覧で「Cloud Firestore」の行を開き、「適用」(Enforce)を押して確定します。
|
|
7
|
+
3. 「Cloud Functions」の行でも同じく「適用」を押します。
|
|
8
|
+
|
|
9
|
+
**うまくいったかの見分け方**: 両方の行が「適用済み」(Enforced)になります。
|
|
10
|
+
|
|
11
|
+
**注意**: 適用の前にアプリの公開が済んでいないと、アプリ自身も断られます。順番はこちらで確認済みです。
|
|
12
|
+
|
|
13
|
+
終わったら「済み」と返信してください。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# プロジェクトを従量課金(Blaze プラン)にする
|
|
2
|
+
|
|
3
|
+
サーバー側の処理(Cloud Functions)を動かすには、プロジェクトに支払い方法をつなぐ必要があります。少人数で使う規模なら、たいていは無料枠に収まります。
|
|
4
|
+
|
|
5
|
+
1. 次のページを開きます: https://console.firebase.google.com/project/{{PROJECT_ID}}/usage/details
|
|
6
|
+
2. 「プランを変更」(Modify plan)を押します。
|
|
7
|
+
3. 「Blaze」を選び、使う請求先アカウント(Cloud Billing アカウント)を選びます。まだ無ければ、画面の案内に従ってカードを登録します。
|
|
8
|
+
4. 予算の設定を聞かれたら、スペックで決めた金額を入れておくと安心です(あとからでも設定できます)。
|
|
9
|
+
5. 「購入」(Purchase)で確定します。
|
|
10
|
+
|
|
11
|
+
**うまくいったかの見分け方**: 同じページの上部に「Blaze」と表示されます。
|
|
12
|
+
|
|
13
|
+
プロジェクトが2つあるときは、もう一方でも同じことをします。終わったら「済み」と返信してください。
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Google ログインを有効にする
|
|
2
|
+
|
|
3
|
+
アプリで「Google でログイン」を使えるようにします。これを有効にするとログイン用の認証情報が作られるので、コンソールで一度だけ操作が必要です。
|
|
4
|
+
|
|
5
|
+
1. 次のページを開きます: https://console.firebase.google.com/project/{{PROJECT_ID}}/authentication/providers
|
|
6
|
+
2. 「始める」(Get started)が出ていたら押します。
|
|
7
|
+
3. 「新しいプロバイダを追加」(Add new provider)から「Google」を選びます。
|
|
8
|
+
4. 右上のスイッチを「有効にする」(Enable)にします。
|
|
9
|
+
5. 「プロジェクトのサポートメール」(Support email)で自分のアドレスを選びます。
|
|
10
|
+
6. 「保存」(Save)を押します。
|
|
11
|
+
|
|
12
|
+
**うまくいったかの見分け方**: 一覧の「Google」の行が「有効」(Enabled)になります。
|
|
13
|
+
|
|
14
|
+
プロジェクトが2つあるときは、もう一方でも同じことをします。終わったら「済み」と返信してください。
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Firebase と Google Cloud にログインする
|
|
2
|
+
|
|
3
|
+
このあと作業するために、あなたの Google アカウントで2つの道具にログインします。パスワードをこの画面に書く必要はありません。
|
|
4
|
+
|
|
5
|
+
1. ターミナルで `firebase login` が動くと、ブラウザが開きます。
|
|
6
|
+
2. 使う Google アカウントを選び、「許可」(Allow)を押します。
|
|
7
|
+
3. 続けて `gcloud auth login` でもう一度ブラウザが開きます。**1 と同じアカウント**を選び、「許可」を押します。
|
|
8
|
+
4. ブラウザに「ログインしました」のような表示が出たら、そのタブは閉じてかまいません。
|
|
9
|
+
|
|
10
|
+
**うまくいったかの見分け方**: ターミナルに `Success! Logged in as あなたのアドレス` と出ます。
|
|
11
|
+
|
|
12
|
+
**困ったら**: 違うアカウントを選んでしまったときは、そのことをそのまま返信してください。やり直します。
|
|
13
|
+
|
|
14
|
+
終わったら「済み」と返信してください。
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"firestore": {
|
|
3
|
+
"rules": "firestore.rules",
|
|
4
|
+
"indexes": "firestore.indexes.json"
|
|
5
|
+
},
|
|
6
|
+
"functions": [
|
|
7
|
+
{
|
|
8
|
+
"source": "functions",
|
|
9
|
+
"codebase": "default",
|
|
10
|
+
"predeploy": ["yarn --cwd \"$RESOURCE_DIR\" build"]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"hosting": {
|
|
14
|
+
"public": "dist",
|
|
15
|
+
"ignore": ["firebase.json", "**/.*"],
|
|
16
|
+
"rewrites": [
|
|
17
|
+
{
|
|
18
|
+
"source": "**",
|
|
19
|
+
"destination": "/index.html"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"headers": [
|
|
23
|
+
{
|
|
24
|
+
"source": "**",
|
|
25
|
+
"headers": [
|
|
26
|
+
{
|
|
27
|
+
"key": "X-Content-Type-Options",
|
|
28
|
+
"value": "nosniff"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"key": "Referrer-Policy",
|
|
32
|
+
"value": "strict-origin-when-cross-origin"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"key": "X-Frame-Options",
|
|
36
|
+
"value": "DENY"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"emulators": {
|
|
43
|
+
"auth": {
|
|
44
|
+
"port": 9099
|
|
45
|
+
},
|
|
46
|
+
"firestore": {
|
|
47
|
+
"port": 8080
|
|
48
|
+
},
|
|
49
|
+
"functions": {
|
|
50
|
+
"port": 5001
|
|
51
|
+
},
|
|
52
|
+
"hosting": {
|
|
53
|
+
"port": 5000
|
|
54
|
+
},
|
|
55
|
+
"ui": {
|
|
56
|
+
"enabled": true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "base",
|
|
3
|
+
"slug": "firebase",
|
|
4
|
+
"title": "Firebase",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Firebase Hosting + Authentication + Firestore + Cloud Functions, with separate dev and production projects.",
|
|
7
|
+
"platform": "firebase",
|
|
8
|
+
"requires": [
|
|
9
|
+
{
|
|
10
|
+
"command": "node",
|
|
11
|
+
"probe": "node --version",
|
|
12
|
+
"installHint": "https://nodejs.org/"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"command": "yarn",
|
|
16
|
+
"probe": "yarn --version",
|
|
17
|
+
"installHint": "corepack enable"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"command": "firebase",
|
|
21
|
+
"probe": "firebase --version",
|
|
22
|
+
"installHint": "yarn global add firebase-tools"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"command": "gcloud",
|
|
26
|
+
"probe": "gcloud --version",
|
|
27
|
+
"installHint": "https://cloud.google.com/sdk/docs/install"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"command": "java",
|
|
31
|
+
"probe": "sh \"$BLUEPRINT_BASE/checks/java21.sh\"",
|
|
32
|
+
"installHint": "The Firebase emulators need JDK 21 or later (macOS: brew install openjdk@21)."
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"credentials": [
|
|
36
|
+
"firebase login",
|
|
37
|
+
"gcloud auth login"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"steps": [
|
|
3
|
+
{
|
|
4
|
+
"id": "spec",
|
|
5
|
+
"title": "仕様書を書く",
|
|
6
|
+
"description": "ヒアリングの答えから、土台と用途の仕様の雛形を埋める。決まっていない点は一覧にする。",
|
|
7
|
+
"skill": "skills/spec",
|
|
8
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/spec.sh\""
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "prerequisites",
|
|
12
|
+
"title": "道具とサインイン",
|
|
13
|
+
"description": "CLI を入れ、Firebase と Google Cloud にサインインする。",
|
|
14
|
+
"skill": "skills/prerequisites",
|
|
15
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/prerequisites.sh\"",
|
|
16
|
+
"gates": ["review", "credential"]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "projects",
|
|
20
|
+
"title": "開発用と本番用のプロジェクト",
|
|
21
|
+
"description": "Firebase のプロジェクトを二つ用意し、従量課金(Blaze)プランにする。",
|
|
22
|
+
"skill": "skills/projects",
|
|
23
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/projects.sh\"",
|
|
24
|
+
"gates": ["billing"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "scaffold",
|
|
28
|
+
"title": "アプリの雛形",
|
|
29
|
+
"description": "画面、Cloud Functions、エミュレータの設定、ルールの試験の土台を作る。",
|
|
30
|
+
"skill": "skills/scaffold",
|
|
31
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/scaffold.sh\""
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "auth",
|
|
35
|
+
"title": "ログイン",
|
|
36
|
+
"description": "Google でのログインを入れ、Auth エミュレータで確かめる。",
|
|
37
|
+
"skill": "skills/auth",
|
|
38
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/emulator-test.sh\" auth"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "data-rules",
|
|
42
|
+
"title": "データの形とセキュリティルール",
|
|
43
|
+
"description": "仕様のコレクションを作り、firestore.rules で一つずつ開ける。それ以外はすべて拒否する。",
|
|
44
|
+
"skill": "skills/data-rules",
|
|
45
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/emulator-test.sh\" rules"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "deploy-dev",
|
|
49
|
+
"title": "開発用に公開",
|
|
50
|
+
"description": "ルール・関数・画面を開発用のプロジェクトに公開する。",
|
|
51
|
+
"skill": "skills/deploy-dev",
|
|
52
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/deploy-check.sh\" dev"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "protect",
|
|
56
|
+
"title": "App Check と予算アラート",
|
|
57
|
+
"description": "App Check を有効にし、本番のプロジェクトに予算アラートを設定する。",
|
|
58
|
+
"skill": "skills/protect",
|
|
59
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/protect.sh\"",
|
|
60
|
+
"gates": ["billing"]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "deploy-production",
|
|
64
|
+
"title": "本番に公開",
|
|
65
|
+
"description": "開発用で確かめたものと同じビルドを本番に公開する。",
|
|
66
|
+
"skill": "skills/deploy-production",
|
|
67
|
+
"check": "sh \"$BLUEPRINT_BASE/checks/deploy-check.sh\" prod",
|
|
68
|
+
"gates": ["deploy-production"]
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Firebase — 必ず詰める点
|
|
2
|
+
|
|
3
|
+
- [ ] **ルールは既定で拒否**。`match /{document=**} { allow read, write: if false; }` を最後に置き、コレクションごとに開ける
|
|
4
|
+
- [ ] **ルールは試験で証明する**。エミュレータ上で「許されるべき操作が通る」と「拒まれるべき操作が拒まれる」の両方を書く。本番のプロジェクトに対して試さない
|
|
5
|
+
- [ ] **クライアントに管理者の鍵を置かない**。サービスアカウントの JSON をリポジトリにも画面にも入れない。Admin SDK は Cloud Functions の中だけで使う
|
|
6
|
+
- [ ] **利用者が書ける文書に権限を置かない**。役割はカスタムクレームで持ち、付与は関数だけが行う
|
|
7
|
+
- [ ] **App Check を有効にする**。Firestore と Functions の両方で強制する
|
|
8
|
+
- [ ] **開発と本番を分ける**。本番のデータを開発に持ち込まない
|
|
9
|
+
- [ ] **予算アラート**を本番に設定する。暴走した関数や読み取りの嵐は請求で初めて気づくことが多い
|
|
10
|
+
- [ ] **Firestore のリージョン**は作成時に決まり、後から移せない。データの置き場所の制約を確認してから作る
|
|
11
|
+
- [ ] `firebase.json` の Hosting に、`Content-Security-Policy` などのセキュリティヘッダーを付ける
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blueprint-firebase-auth
|
|
3
|
+
description: "Add Google sign-in to the app and prove it against the Auth emulator."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sign-in
|
|
7
|
+
|
|
8
|
+
1. Google sign-in with `signInWithPopup` (fall back to redirect on mobile).
|
|
9
|
+
2. Point the app at the Auth emulator when `import.meta.env.DEV` is set.
|
|
10
|
+
3. A signed-out user sees only the sign-in screen.
|
|
11
|
+
4. Write `test/blueprint/auth.spec.ts`: a signed-in context can read what the rules allow a member, an
|
|
12
|
+
unauthenticated context cannot.
|
|
13
|
+
|
|
14
|
+
Done when the check passes: `emulator-test.sh auth`.
|
|
15
|
+
|
|
16
|
+
## Always
|
|
17
|
+
|
|
18
|
+
- Read `.blueprint/spec.md` first. It is the agreed specification; do not widen it.
|
|
19
|
+
- When you need a decision, ask it through the blueprint question tool and stop. Do not guess.
|
|
20
|
+
- Never enable billing, deploy to production, delete data or handle a credential unless this step's gates say the
|
|
21
|
+
user approved exactly that.
|
|
22
|
+
- Say you are done by stopping; the executor runs the check. Do not claim success yourself.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blueprint-firebase-data-rules
|
|
3
|
+
description: "Model the collections from the spec and open each one explicitly in firestore.rules, proven by emulator tests."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Data model and security rules
|
|
7
|
+
|
|
8
|
+
The spec has a table per collection: who may read, create, update and delete. That table IS the rules.
|
|
9
|
+
|
|
10
|
+
1. For each collection, add a `match` block above the final deny-all. Open only what the table says.
|
|
11
|
+
2. On create, pin ownership (`request.resource.data.ownerUid == request.auth.uid`) and the fields a client may set
|
|
12
|
+
(`request.resource.data.keys().hasOnly([...])`).
|
|
13
|
+
3. On update, forbid changing ownership and any field the table does not let that role change
|
|
14
|
+
(`diff(resource.data).affectedKeys().hasOnly([...])`).
|
|
15
|
+
4. Write `test/blueprint/rules.spec.ts` with BOTH directions for every row of the table: the allowed operation
|
|
16
|
+
succeeds (`assertSucceeds`) and every other role is refused (`assertFails`). A rule with only a success test is
|
|
17
|
+
not tested.
|
|
18
|
+
5. If the table is ambiguous, ask — do not open more than it says.
|
|
19
|
+
|
|
20
|
+
Done when the check passes: `emulator-test.sh rules`.
|
|
21
|
+
|
|
22
|
+
## Always
|
|
23
|
+
|
|
24
|
+
- Read `.blueprint/spec.md` first. It is the agreed specification; do not widen it.
|
|
25
|
+
- When you need a decision, ask it through the blueprint question tool and stop. Do not guess.
|
|
26
|
+
- Never enable billing, deploy to production, delete data or handle a credential unless this step's gates say the
|
|
27
|
+
user approved exactly that.
|
|
28
|
+
- Say you are done by stopping; the executor runs the check. Do not claim success yourself.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blueprint-firebase-deploy-dev
|
|
3
|
+
description: "Deploy rules, indexes, functions and hosting to the dev project."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Publish to dev
|
|
7
|
+
|
|
8
|
+
1. `yarn build`, then give this build an id and ship it with the site:
|
|
9
|
+
`node -e 'console.log(require("crypto").randomUUID())' > .blueprint/build-id && cp .blueprint/build-id dist/blueprint-build.txt`,
|
|
10
|
+
then `firebase deploy --project dev`. The check compares the served id with `.blueprint/build-id`, so it
|
|
11
|
+
proves THIS build is live. Git is not needed for this.
|
|
12
|
+
2. Tell the user the URL (`https://<dev-project-id>.web.app`) and what to try there.
|
|
13
|
+
|
|
14
|
+
Never deploy to `prod` in this step.
|
|
15
|
+
|
|
16
|
+
Done when the check passes: the dev site serves the id this deploy wrote to `.blueprint/build-id`, serves a web
|
|
17
|
+
app config at `/__/firebase/init.json`, has Google sign-in switched on, and the page renders text in headless Chrome (a blank page fails).
|
|
18
|
+
|
|
19
|
+
## Always
|
|
20
|
+
|
|
21
|
+
- Read `.blueprint/spec.md` first. It is the agreed specification; do not widen it.
|
|
22
|
+
- When you need a decision, ask it through the blueprint question tool and stop. Do not guess.
|
|
23
|
+
- Never enable billing, deploy to production, delete data or handle a credential unless this step's gates say the
|
|
24
|
+
user approved exactly that.
|
|
25
|
+
- Say you are done by stopping; the executor runs the check. Do not claim success yourself.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blueprint-firebase-deploy-production
|
|
3
|
+
description: "Deploy the same build to the production project."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Publish to production
|
|
7
|
+
|
|
8
|
+
This is the **deploy-production** gate: the user has approved it. Deploy the same source that was verified on dev — do not change anything between the two.
|
|
9
|
+
|
|
10
|
+
1. `yarn build`, then give this build an id and ship it with the site:
|
|
11
|
+
`node -e 'console.log(require("crypto").randomUUID())' > .blueprint/build-id && cp .blueprint/build-id dist/blueprint-build.txt`,
|
|
12
|
+
then `firebase deploy --project prod`. The check compares the served id with `.blueprint/build-id`, so it
|
|
13
|
+
proves THIS build is live. Git is not needed for this.
|
|
14
|
+
2. Tell the user the URL and that the next change should go through dev first.
|
|
15
|
+
|
|
16
|
+
Done when the check passes: the production site serves the id this deploy wrote to `.blueprint/build-id`.
|
|
17
|
+
|
|
18
|
+
## Always
|
|
19
|
+
|
|
20
|
+
- Read `.blueprint/spec.md` first. It is the agreed specification; do not widen it.
|
|
21
|
+
- When you need a decision, ask it through the blueprint question tool and stop. Do not guess.
|
|
22
|
+
- Never enable billing, deploy to production, delete data or handle a credential unless this step's gates say the
|
|
23
|
+
user approved exactly that.
|
|
24
|
+
- Say you are done by stopping; the executor runs the check. Do not claim success yourself.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blueprint-firebase-prerequisites
|
|
3
|
+
description: "Check and install the tools a Firebase blueprint needs, and get the user signed in to Firebase and Google Cloud."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tools and sign-in
|
|
7
|
+
|
|
8
|
+
The user is not an engineer. Explain each thing you install in one plain sentence.
|
|
9
|
+
|
|
10
|
+
1. For every entry in `requires` of the base manifest, run its `probe`. Install what is missing using its `installHint`.
|
|
11
|
+
The emulators need **JDK 21 or later**; an older `java` on PATH does not count. On macOS
|
|
12
|
+
`brew install openjdk@21` is enough — the checks find it without changing the user's default `java`.
|
|
13
|
+
2. Firebase and gcloud must be signed in to the **same** account: the checks use both. If they differ, ask
|
|
14
|
+
which account to use.
|
|
15
|
+
3. Sign-in is the **credential** gate: the executor has already asked the user to approve it. Run `firebase login` and
|
|
16
|
+
`gcloud auth login` in the terminal and tell the user a browser window will open. Never ask the user to paste a
|
|
17
|
+
password, token or key into the chat.
|
|
18
|
+
4. Never create or download a service-account key. Nothing in this blueprint needs one on this machine.
|
|
19
|
+
|
|
20
|
+
Done when the check passes: every tool answers, a JDK 21+ is found, and Firebase and gcloud are signed in to
|
|
21
|
+
the same account with a live session.
|
|
22
|
+
|
|
23
|
+
## Always
|
|
24
|
+
|
|
25
|
+
- Read `.blueprint/spec.md` first. It is the agreed specification; do not widen it.
|
|
26
|
+
- When you need a decision, ask it through the blueprint question tool and stop. Do not guess.
|
|
27
|
+
- Never enable billing, deploy to production, delete data or handle a credential unless this step's gates say the
|
|
28
|
+
user approved exactly that.
|
|
29
|
+
- Say you are done by stopping; the executor runs the check. Do not claim success yourself.
|