@reddoorla/maintenance 0.76.0 → 0.79.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.
Files changed (125) hide show
  1. package/README.md +34 -7
  2. package/dist/{blux-RIJBRVCF.js → blux-2P4FGCUL.js} +5 -5
  3. package/dist/{blux-RIJBRVCF.js.map → blux-2P4FGCUL.js.map} +1 -1
  4. package/dist/{bump-deps-OQR3IXLN.js → bump-deps-XB2LJTVN.js} +3 -3
  5. package/dist/{chunk-RHPDQW6D.js → chunk-2SLED3NB.js} +1 -1
  6. package/dist/{chunk-RHPDQW6D.js.map → chunk-2SLED3NB.js.map} +1 -1
  7. package/dist/{chunk-3U3IGVAQ.js → chunk-3LWKIKZ2.js} +47 -9
  8. package/dist/chunk-3LWKIKZ2.js.map +1 -0
  9. package/dist/{chunk-HIN3P4ZN.js → chunk-4SOCPK7A.js} +10 -1
  10. package/dist/chunk-4SOCPK7A.js.map +1 -0
  11. package/dist/{chunk-CASCWQQX.js → chunk-73UFRZKY.js} +34 -19
  12. package/dist/chunk-73UFRZKY.js.map +1 -0
  13. package/dist/chunk-A3WDWNN5.js +70 -0
  14. package/dist/chunk-A3WDWNN5.js.map +1 -0
  15. package/dist/{chunk-D3DQFC32.js → chunk-ATJ6ZA2V.js} +2 -2
  16. package/dist/{chunk-ZO7WNY57.js → chunk-B5ZUQZFL.js} +2 -2
  17. package/dist/{chunk-PH3AGF4Z.js → chunk-DC5H7KEQ.js} +2 -2
  18. package/dist/{chunk-DYKG53IR.js → chunk-FAQIMC26.js} +9 -9
  19. package/dist/{chunk-4VAUSRSS.js → chunk-FVVGDIGX.js} +2 -2
  20. package/dist/{chunk-7MJBTV2R.js → chunk-FX7RAMGC.js} +2 -2
  21. package/dist/{chunk-PKVZMFEB.js → chunk-K5JZ5KLO.js} +2 -2
  22. package/dist/{chunk-CZAMYVOP.js → chunk-KPQXB5CR.js} +2 -2
  23. package/dist/{chunk-BTEBRTW4.js → chunk-LS6QPANO.js} +2 -2
  24. package/dist/{chunk-2X322ZES.js → chunk-MGJ72BA4.js} +2 -2
  25. package/dist/chunk-MNDIED4M.js +137 -0
  26. package/dist/chunk-MNDIED4M.js.map +1 -0
  27. package/dist/{chunk-ZYBOT5PM.js → chunk-NT4WIP7I.js} +2 -2
  28. package/dist/{chunk-JNXCDUYB.js → chunk-Q276MZ7E.js} +3 -4
  29. package/dist/chunk-Q276MZ7E.js.map +1 -0
  30. package/dist/{chunk-GYNRZCM6.js → chunk-QG6OSL4M.js} +2 -2
  31. package/dist/{chunk-IRRZG66U.js → chunk-QMXQUL5Z.js} +34 -18
  32. package/dist/chunk-QMXQUL5Z.js.map +1 -0
  33. package/dist/{chunk-YEGHUODU.js → chunk-VSHC4HOI.js} +96 -1
  34. package/dist/chunk-VSHC4HOI.js.map +1 -0
  35. package/dist/{chunk-JTRTG5CU.js → chunk-YIQ7IWE3.js} +2 -2
  36. package/dist/{chunk-XKOZUVK2.js → chunk-ZTPPCHDB.js} +1 -1
  37. package/dist/chunk-ZTPPCHDB.js.map +1 -0
  38. package/dist/cli/bin.js +32 -21
  39. package/dist/cli/bin.js.map +1 -1
  40. package/dist/cli/commands/audit.d.ts +1 -1
  41. package/dist/cli/commands/audit.js +7 -5
  42. package/dist/cli/commands/audit.js.map +1 -1
  43. package/dist/client-X5WUJWDE.js +11 -0
  44. package/dist/configs/eslint.js +10 -0
  45. package/dist/configs/eslint.js.map +1 -1
  46. package/dist/{convert-to-pnpm-OVMASATZ.js → convert-to-pnpm-FZHSLUQB.js} +3 -3
  47. package/dist/{db-3QW2XEXA.js → db-JSIZYT23.js} +3 -3
  48. package/dist/{digest-75FWSC74.js → digest-NQAQI34P.js} +11 -11
  49. package/dist/forms/index.js +6 -62
  50. package/dist/forms/index.js.map +1 -1
  51. package/dist/{github-signals-TF3VSNIN.js → github-signals-4LWQGD5V.js} +5 -5
  52. package/dist/{health-endpoint-JZGYCMTV.js → health-endpoint-LFVE26QS.js} +3 -3
  53. package/dist/index.d.ts +11 -3
  54. package/dist/index.js +32 -24
  55. package/dist/index.js.map +1 -1
  56. package/dist/{init-2KOISBCZ.js → init-Y5O33BUL.js} +14 -12
  57. package/dist/{init-2KOISBCZ.js.map → init-Y5O33BUL.js.map} +1 -1
  58. package/dist/{launch-K4EHKTXC.js → launch-3SYLX263.js} +8 -5
  59. package/dist/launch-3SYLX263.js.map +1 -0
  60. package/dist/migrate-LJUFGXQZ.js +7 -0
  61. package/dist/{onboard-PX36YDZA.js → onboard-RDGHVCNG.js} +3 -3
  62. package/dist/{orchestrate-CPR6KVS2.js → orchestrate-QOQXOJLI.js} +5 -5
  63. package/dist/{preflight-QRRMPI7K.js → preflight-2PWVVIRR.js} +6 -6
  64. package/dist/protection-audit-X3BHT2MB.js +140 -0
  65. package/dist/protection-audit-X3BHT2MB.js.map +1 -0
  66. package/dist/recipes/sync-configs.d.ts +1 -1
  67. package/dist/recipes/sync-configs.js +3 -3
  68. package/dist/{renovate-dispatch-6II2JVOX.js → renovate-dispatch-2VYCEH42.js} +2 -2
  69. package/dist/renovate-dispatch-2VYCEH42.js.map +1 -0
  70. package/dist/{report-SLGBDJ6O.js → report-IU4CLJS5.js} +3 -3
  71. package/dist/{self-updating-2YSVNMPP.js → self-updating-ALMUWJNX.js} +5 -4
  72. package/dist/{self-updating-2YSVNMPP.js.map → self-updating-ALMUWJNX.js.map} +1 -1
  73. package/dist/{selftest-RVASRO3Z.js → selftest-PJFABZDM.js} +5 -5
  74. package/dist/{smoke-suite-CSP23NZM.js → smoke-suite-SUEJFBYP.js} +3 -3
  75. package/dist/{submissions-MDKWJYMM.js → submissions-3I3GAQAX.js} +13 -5
  76. package/dist/submissions-3I3GAQAX.js.map +1 -0
  77. package/dist/{submissions-BNRGY3JQ.js → submissions-SURN7BV5.js} +3 -3
  78. package/dist/{svelte-codemods-V54KOBWU.js → svelte-codemods-IWO7X3F3.js} +3 -3
  79. package/dist/{sync-configs-UBCJRASZ.js → sync-configs-7XGHFC6P.js} +4 -4
  80. package/dist/{types-0jreMCF5.d.ts → types-DnEL3NRn.d.ts} +1 -1
  81. package/dist/{upgrade-WR6ICPAG.js → upgrade-AM4S7JF3.js} +3 -3
  82. package/package.json +1 -1
  83. package/dist/chunk-3U3IGVAQ.js.map +0 -1
  84. package/dist/chunk-CASCWQQX.js.map +0 -1
  85. package/dist/chunk-HIN3P4ZN.js.map +0 -1
  86. package/dist/chunk-IRRZG66U.js.map +0 -1
  87. package/dist/chunk-JNXCDUYB.js.map +0 -1
  88. package/dist/chunk-XKOZUVK2.js.map +0 -1
  89. package/dist/chunk-YEGHUODU.js.map +0 -1
  90. package/dist/client-NVVQK3HH.js +0 -11
  91. package/dist/launch-K4EHKTXC.js.map +0 -1
  92. package/dist/migrate-35DNNF3H.js +0 -7
  93. package/dist/renovate-dispatch-6II2JVOX.js.map +0 -1
  94. package/dist/submissions-MDKWJYMM.js.map +0 -1
  95. /package/dist/{bump-deps-OQR3IXLN.js.map → bump-deps-XB2LJTVN.js.map} +0 -0
  96. /package/dist/{chunk-D3DQFC32.js.map → chunk-ATJ6ZA2V.js.map} +0 -0
  97. /package/dist/{chunk-ZO7WNY57.js.map → chunk-B5ZUQZFL.js.map} +0 -0
  98. /package/dist/{chunk-PH3AGF4Z.js.map → chunk-DC5H7KEQ.js.map} +0 -0
  99. /package/dist/{chunk-DYKG53IR.js.map → chunk-FAQIMC26.js.map} +0 -0
  100. /package/dist/{chunk-4VAUSRSS.js.map → chunk-FVVGDIGX.js.map} +0 -0
  101. /package/dist/{chunk-7MJBTV2R.js.map → chunk-FX7RAMGC.js.map} +0 -0
  102. /package/dist/{chunk-PKVZMFEB.js.map → chunk-K5JZ5KLO.js.map} +0 -0
  103. /package/dist/{chunk-CZAMYVOP.js.map → chunk-KPQXB5CR.js.map} +0 -0
  104. /package/dist/{chunk-BTEBRTW4.js.map → chunk-LS6QPANO.js.map} +0 -0
  105. /package/dist/{chunk-2X322ZES.js.map → chunk-MGJ72BA4.js.map} +0 -0
  106. /package/dist/{chunk-ZYBOT5PM.js.map → chunk-NT4WIP7I.js.map} +0 -0
  107. /package/dist/{chunk-GYNRZCM6.js.map → chunk-QG6OSL4M.js.map} +0 -0
  108. /package/dist/{chunk-JTRTG5CU.js.map → chunk-YIQ7IWE3.js.map} +0 -0
  109. /package/dist/{client-NVVQK3HH.js.map → client-X5WUJWDE.js.map} +0 -0
  110. /package/dist/{convert-to-pnpm-OVMASATZ.js.map → convert-to-pnpm-FZHSLUQB.js.map} +0 -0
  111. /package/dist/{db-3QW2XEXA.js.map → db-JSIZYT23.js.map} +0 -0
  112. /package/dist/{digest-75FWSC74.js.map → digest-NQAQI34P.js.map} +0 -0
  113. /package/dist/{github-signals-TF3VSNIN.js.map → github-signals-4LWQGD5V.js.map} +0 -0
  114. /package/dist/{health-endpoint-JZGYCMTV.js.map → health-endpoint-LFVE26QS.js.map} +0 -0
  115. /package/dist/{migrate-35DNNF3H.js.map → migrate-LJUFGXQZ.js.map} +0 -0
  116. /package/dist/{onboard-PX36YDZA.js.map → onboard-RDGHVCNG.js.map} +0 -0
  117. /package/dist/{orchestrate-CPR6KVS2.js.map → orchestrate-QOQXOJLI.js.map} +0 -0
  118. /package/dist/{preflight-QRRMPI7K.js.map → preflight-2PWVVIRR.js.map} +0 -0
  119. /package/dist/{report-SLGBDJ6O.js.map → report-IU4CLJS5.js.map} +0 -0
  120. /package/dist/{selftest-RVASRO3Z.js.map → selftest-PJFABZDM.js.map} +0 -0
  121. /package/dist/{smoke-suite-CSP23NZM.js.map → smoke-suite-SUEJFBYP.js.map} +0 -0
  122. /package/dist/{submissions-BNRGY3JQ.js.map → submissions-SURN7BV5.js.map} +0 -0
  123. /package/dist/{svelte-codemods-V54KOBWU.js.map → svelte-codemods-IWO7X3F3.js.map} +0 -0
  124. /package/dist/{sync-configs-UBCJRASZ.js.map → sync-configs-7XGHFC6P.js.map} +0 -0
  125. /package/dist/{upgrade-WR6ICPAG.js.map → upgrade-AM4S7JF3.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db/migrations.ts","../src/db/migrate.ts"],"sourcesContent":["/** Ordered, append-only list of standard-SQL migration scripts. Each runs once,\n * tracked by `id` in the `_migrations` table (see migrate.ts). Statements use\n * IF NOT EXISTS so even a partial re-apply is safe. Never edit a shipped script —\n * add a new one. Every statement in a script MUST be independently idempotent:\n * `migrate.ts` runs each script via `executeMultiple`, which is NOT transactional,\n * so a mid-script failure can leave earlier statements applied and the id unrecorded\n * — a re-run then re-executes the whole script. Standard SQLite SQL only (no\n * Turso-specific syntax) so the host stays a connection-string swap. */\nexport type Migration = { id: string; sql: string };\n\nexport const MIGRATIONS: Migration[] = [\n {\n id: \"0001_init\",\n sql: `\n CREATE TABLE IF NOT EXISTS submissions (\n id TEXT PRIMARY KEY,\n submission_id INTEGER,\n site_id TEXT NOT NULL,\n form_type TEXT NOT NULL,\n name TEXT NOT NULL,\n email TEXT NOT NULL,\n phone TEXT,\n message TEXT,\n extra_fields TEXT,\n source_url TEXT,\n utm TEXT,\n submitted_at TEXT,\n status TEXT NOT NULL DEFAULT 'new',\n notify_status TEXT NOT NULL DEFAULT 'skipped',\n resend_message_id TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_submissions_site_submitted\n ON submissions (site_id, submitted_at DESC);\n CREATE INDEX IF NOT EXISTS idx_submissions_status\n ON submissions (status);\n CREATE TABLE IF NOT EXISTS spam_screenouts (\n site_id TEXT NOT NULL,\n date TEXT NOT NULL,\n honeypot INTEGER NOT NULL DEFAULT 0,\n too_fast INTEGER NOT NULL DEFAULT 0,\n marked_spam INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (site_id, date)\n );\n `,\n },\n {\n id: \"0002_fleet_events\",\n sql: `\n CREATE TABLE IF NOT EXISTS fleet_events (\n id TEXT PRIMARY KEY,\n ts TEXT NOT NULL,\n type TEXT NOT NULL,\n site_id TEXT,\n site_name TEXT,\n summary TEXT NOT NULL,\n data TEXT,\n created_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_fleet_events_ts ON fleet_events (ts);\n `,\n },\n {\n id: \"0003_add_spam_score\",\n // SQLite `ADD COLUMN` has no `IF NOT EXISTS`, so it is NOT self-idempotent: if the\n // `_migrations` marker is lost after the column is added (crash between statement\n // and marker write), a re-run would throw `duplicate column name`. `migrate.ts`\n // recognizes that error as already-applied and records the marker, keeping the\n // runner idempotent (see `isAlreadyAppliedError`).\n sql: `ALTER TABLE submissions ADD COLUMN spam_score REAL;`,\n },\n {\n id: \"0004_add_spam_reason\",\n sql: `ALTER TABLE submissions ADD COLUMN spam_reason TEXT;`,\n },\n {\n // Outcome of the newsletter fan-out (site webhook + Mailchimp). Before this the\n // results were console.error-only, so a rotated Mailchimp key or an outage would\n // silently stop signups reaching the audience with no trace anywhere the operator\n // looks — the row still read `notify=sent`, healthy. Same non-idempotent\n // ADD COLUMN caveat as 0003.\n id: \"0005_add_fanout_status\",\n sql: `ALTER TABLE submissions ADD COLUMN fanout_status TEXT;`,\n },\n];\n","import type { Client } from \"@libsql/client\";\nimport { MIGRATIONS } from \"./migrations.js\";\n\n/** True when an error means \"this DDL was already applied\" and re-applying is a\n * no-op we can safely ignore. `CREATE ... IF NOT EXISTS` never throws, but SQLite\n * `ALTER TABLE ... ADD COLUMN` has no `IF NOT EXISTS`, so a re-run after a lost\n * `_migrations` marker throws `duplicate column name: …`. Treating that as\n * already-applied keeps `runMigrations` idempotent for `ADD COLUMN` migrations —\n * without it a lost marker would boot-loop `openDb()` on every call. */\nfunction isAlreadyAppliedError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err);\n return /duplicate column name/i.test(message);\n}\n\n/** Apply every not-yet-applied migration in order against a raw libSQL client,\n * tracking applied ids in `_migrations`. Idempotent: ids already present are\n * skipped, the DDL uses IF NOT EXISTS where it can, and an already-applied\n * `ADD COLUMN` (recognized by its error) is swallowed so the marker is still\n * recorded. Returns the ids applied this run. Runs on every openDb() — cheap\n * (one indexed SELECT) and the gate every fresh Turso database needs before its\n * first write. */\nexport async function runMigrations(client: Client): Promise<string[]> {\n await client.execute(\n \"CREATE TABLE IF NOT EXISTS _migrations (id TEXT PRIMARY KEY, applied_at TEXT NOT NULL)\",\n );\n const existing = await client.execute(\"SELECT id FROM _migrations\");\n const applied = new Set(existing.rows.map((r) => String(r.id)));\n const ran: string[] = [];\n for (const m of MIGRATIONS) {\n if (applied.has(m.id)) continue;\n try {\n await client.executeMultiple(m.sql);\n } catch (err) {\n // A re-run after a lost marker can re-execute a non-idempotent `ADD COLUMN`;\n // treat \"already applied\" as success so we still record the marker below and\n // never boot-loop. Any other error propagates unchanged.\n if (!isAlreadyAppliedError(err)) throw err;\n }\n await client.execute({\n sql: \"INSERT INTO _migrations (id, applied_at) VALUES (?, ?)\",\n args: [m.id, new Date().toISOString()],\n });\n ran.push(m.id);\n }\n return ran;\n}\n"],"mappings":";AAUO,IAAM,aAA0B;AAAA,EACrC;AAAA,IACE,IAAI;AAAA,IACJ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BP;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaP;AAAA,EACA;AAAA,IACE,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMJ,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,KAAK;AAAA,EACP;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAME,IAAI;AAAA,IACJ,KAAK;AAAA,EACP;AACF;;;AC1EA,SAAS,sBAAsB,KAAuB;AACpD,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,SAAO,yBAAyB,KAAK,OAAO;AAC9C;AASA,eAAsB,cAAc,QAAmC;AACrE,QAAM,OAAO;AAAA,IACX;AAAA,EACF;AACA,QAAM,WAAW,MAAM,OAAO,QAAQ,4BAA4B;AAClE,QAAM,UAAU,IAAI,IAAI,SAAS,KAAK,IAAI,CAAC,MAAM,OAAO,EAAE,EAAE,CAAC,CAAC;AAC9D,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,YAAY;AAC1B,QAAI,QAAQ,IAAI,EAAE,EAAE,EAAG;AACvB,QAAI;AACF,YAAM,OAAO,gBAAgB,EAAE,GAAG;AAAA,IACpC,SAAS,KAAK;AAIZ,UAAI,CAAC,sBAAsB,GAAG,EAAG,OAAM;AAAA,IACzC;AACA,UAAM,OAAO,QAAQ;AAAA,MACnB,KAAK;AAAA,MACL,MAAM,CAAC,EAAE,KAAI,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA,IACvC,CAAC;AACD,QAAI,KAAK,EAAE,EAAE;AAAA,EACf;AACA,SAAO;AACT;","names":[]}
@@ -60,37 +60,53 @@ export default createSvelteConfig({
60
60
  });
61
61
  `
62
62
  };
63
- var ci = {
64
- config: "ci",
65
- path: ".github/workflows/ci.yml",
66
- contents: `name: ci
67
- on:
68
- pull_request:
69
- push:
70
- branches: [main]
71
- jobs:
72
- ci:
73
- uses: reddoorla/.github/.github/workflows/ci.yml@78c4da64b675f0f474961f12715f2a4c09d46eb5 # v1.0.0
74
- `
75
- };
76
63
  var renovateAction = {
77
64
  config: "renovate-action",
78
65
  path: ".github/workflows/renovate.yml",
79
66
  contents: `name: renovate
80
67
  on:
81
68
  schedule:
82
- - cron: "0 7 * * 1"
69
+ # Twice daily. GitHub's platform auto-merge is disabled fleet-wide, so Renovate
70
+ # performs its own merges from inside this run \u2014 which means this cron is the
71
+ # MERGE cadence, not just the PR-creation cadence.
72
+ - cron: 0 */12 * * *
83
73
  workflow_dispatch:
74
+ permissions:
75
+ contents: read
84
76
  jobs:
85
77
  renovate:
86
78
  runs-on: ubuntu-latest
87
79
  steps:
88
- - uses: actions/checkout@v4
89
- - uses: renovatebot/github-action@v46.1.14
80
+ # Renovate authenticates as the reddoor-renovate GitHub App (installed
81
+ # org-wide, "All repositories" \u2014 future repos are auto-covered), NOT as an
82
+ # operator PAT. Why: the App is a distinct identity, so the audit trail
83
+ # finally distinguishes bot from human (the 2026-07-26 unreviewed-major
84
+ # forensics took hours precisely because it couldn't); the credential is
85
+ # permission-scoped and centrally revocable instead of a fleet-write PAT;
86
+ # and the installation gets its own API-quota pool instead of sharing the
87
+ # operator's. The minted token is scoped to THIS repository only
88
+ # (create-github-app-token's default when owner/repositories are omitted).
89
+ # RENOVATE_APP_ID / RENOVATE_APP_PRIVATE_KEY are org-level (all-repos
90
+ # visibility) \u2014 nothing to configure per repo.
91
+ # Digest-pinned: this workflow mints a repo-write token, so a mutable tag
92
+ # ref here is a supply-chain hole \u2014 a retagged \`@v3\`/\`@v46\` would run
93
+ # attacker code with that token. Pin to the commit; Renovate bumps these.
94
+ - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
95
+ id: app-token
96
+ with:
97
+ app-id: \${{ vars.RENOVATE_APP_ID }}
98
+ private-key: \${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
99
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
100
+ - uses: renovatebot/github-action@1a96852b0384df1837619d04c60b2d10d1f9ff08 # v46.1.21
90
101
  with:
91
- token: \${{ secrets.RENOVATE_TOKEN }}
102
+ token: \${{ steps.app-token.outputs.token }}
92
103
  env:
93
104
  RENOVATE_REPOSITORIES: \${{ github.repository }}
105
+ # Commit/PR identity = the App's bot user, so gitAuthor matches the
106
+ # token identity (mismatched authors read as "operator pushed this").
107
+ # 312185038 = the bot's user id (gh api 'users/reddoor-renovate[bot]' --jq .id).
108
+ RENOVATE_USERNAME: reddoor-renovate[bot]
109
+ RENOVATE_GIT_AUTHOR: reddoor-renovate[bot] <312185038+reddoor-renovate[bot]@users.noreply.github.com>
94
110
  `
95
111
  };
96
112
  var renovateConfig = {
@@ -145,7 +161,6 @@ var ALL_TEMPLATES = [
145
161
  lighthouse,
146
162
  playwrightA11y,
147
163
  svelte,
148
- ci,
149
164
  renovateAction,
150
165
  renovateConfig,
151
166
  netlify
@@ -158,4 +173,4 @@ export {
158
173
  ALL_TEMPLATES,
159
174
  templatesByName
160
175
  };
161
- //# sourceMappingURL=chunk-CASCWQQX.js.map
176
+ //# sourceMappingURL=chunk-73UFRZKY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recipes/sync-configs/templates.ts"],"sourcesContent":["import type { ConfigName } from \"../../types.js\";\n\nexport type ConfigTemplate = {\n config: ConfigName;\n path: string;\n contents: string;\n};\n\nconst eslint: ConfigTemplate = {\n config: \"eslint\",\n path: \"eslint.config.js\",\n contents: `import { createEslintConfig } from \"@reddoorla/maintenance/configs/eslint\";\nimport svelteConfig from \"./svelte.config.js\";\n\nexport default createEslintConfig({ svelteConfig });\n`,\n};\n\nconst prettier: ConfigTemplate = {\n config: \"prettier\",\n path: \".prettierrc.json\",\n contents: `{\n \"trailingComma\": \"all\",\n \"singleQuote\": false,\n \"printWidth\": 100,\n \"plugins\": [\"prettier-plugin-svelte\"]\n}\n`,\n};\n\nconst prettierIgnore: ConfigTemplate = {\n config: \"prettier-ignore\",\n path: \".prettierignore\",\n contents: `pnpm-lock.yaml\n.svelte-kit/\nbuild/\n.netlify/\ndist/\n`,\n};\n\nconst lighthouse: ConfigTemplate = {\n config: \"lighthouse\",\n path: \"lighthouserc.json\",\n contents: `${JSON.stringify(\n {\n $note:\n \"Generated by @reddoorla/maintenance sync-configs; edit src/configs/lighthouse.ts in the package instead.\",\n extends: \"@reddoorla/maintenance/configs/lighthouse\",\n },\n null,\n 2,\n )}\n`,\n};\n\nconst playwrightA11y: ConfigTemplate = {\n config: \"playwright-a11y\",\n path: \"playwright.config.ts\",\n contents: `export { default } from \"@reddoorla/maintenance/configs/playwright-a11y\";\n`,\n};\n\nconst svelte: ConfigTemplate = {\n config: \"svelte\",\n path: \"svelte.config.js\",\n contents: `import { createSvelteConfig } from \"@reddoorla/maintenance/configs/svelte\";\nimport adapter from \"@sveltejs/adapter-netlify\";\n\n/** @type {import('@sveltejs/kit').Config} */\nexport default createSvelteConfig({\n kit: { adapter: adapter({ edge: false, split: false }) },\n});\n`,\n};\n\n// There is deliberately NO ci.yml template. Every live fleet ci.yml carries\n// per-site values a static template cannot own (`netlify-site:`, plus\n// `permissions:` and `node-version:` inputs) — an exact-match heal against a\n// canonical byte template would strip them fleet-wide in green auto-mergeable\n// PRs (the \"armed clobber\" the 2026-08-02 architecture review flagged; it was\n// this file's `ci` entry). Ownership is split instead: the STARTER clone\n// provides each site's ci.yml shape at bootstrap, and Renovate's\n// github-actions manager bumps the pinned reusable-workflow ref per repo when\n// reddoorla/.github tags a new version. The caller job name `ci` + the\n// reusable workflow's job `ci` produce the check context \"ci / ci\" —\n// REQUIRED_CHECK in src/recipes/self-updating/index.ts depends on that naming.\n\nconst renovateAction: ConfigTemplate = {\n config: \"renovate-action\",\n path: \".github/workflows/renovate.yml\",\n contents: `name: renovate\non:\n schedule:\n # Twice daily. GitHub's platform auto-merge is disabled fleet-wide, so Renovate\n # performs its own merges from inside this run — which means this cron is the\n # MERGE cadence, not just the PR-creation cadence.\n - cron: 0 */12 * * *\n workflow_dispatch:\npermissions:\n contents: read\njobs:\n renovate:\n runs-on: ubuntu-latest\n steps:\n # Renovate authenticates as the reddoor-renovate GitHub App (installed\n # org-wide, \"All repositories\" — future repos are auto-covered), NOT as an\n # operator PAT. Why: the App is a distinct identity, so the audit trail\n # finally distinguishes bot from human (the 2026-07-26 unreviewed-major\n # forensics took hours precisely because it couldn't); the credential is\n # permission-scoped and centrally revocable instead of a fleet-write PAT;\n # and the installation gets its own API-quota pool instead of sharing the\n # operator's. The minted token is scoped to THIS repository only\n # (create-github-app-token's default when owner/repositories are omitted).\n # RENOVATE_APP_ID / RENOVATE_APP_PRIVATE_KEY are org-level (all-repos\n # visibility) — nothing to configure per repo.\n # Digest-pinned: this workflow mints a repo-write token, so a mutable tag\n # ref here is a supply-chain hole — a retagged \\`@v3\\`/\\`@v46\\` would run\n # attacker code with that token. Pin to the commit; Renovate bumps these.\n - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0\n id: app-token\n with:\n app-id: \\${{ vars.RENOVATE_APP_ID }}\n private-key: \\${{ secrets.RENOVATE_APP_PRIVATE_KEY }}\n - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7\n - uses: renovatebot/github-action@1a96852b0384df1837619d04c60b2d10d1f9ff08 # v46.1.21\n with:\n token: \\${{ steps.app-token.outputs.token }}\n env:\n RENOVATE_REPOSITORIES: \\${{ github.repository }}\n # Commit/PR identity = the App's bot user, so gitAuthor matches the\n # token identity (mismatched authors read as \"operator pushed this\").\n # 312185038 = the bot's user id (gh api 'users/reddoor-renovate[bot]' --jq .id).\n RENOVATE_USERNAME: reddoor-renovate[bot]\n RENOVATE_GIT_AUTHOR: reddoor-renovate[bot] <312185038+reddoor-renovate[bot]@users.noreply.github.com>\n`,\n};\n\nconst renovateConfig: ConfigTemplate = {\n config: \"renovate-config\",\n path: \"renovate.json\",\n contents: `{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\"github>reddoorla/.github:renovate-config\"]\n}\n`,\n};\n\nconst netlify: ConfigTemplate = {\n config: \"netlify\",\n path: \"netlify.toml\",\n contents: `[build]\n command = \"pnpm build\"\n publish = \"build/\"\n functions = \"functions/\"\n\n[build.environment]\n NODE_VERSION = \"22\"\n COREPACK_INTEGRITY_KEYS = \"0\"\n\n# Baseline security headers for all responses. CSP is emitted per-response by\n# SvelteKit (see \\`kit.csp\\` in svelte.config.js) so it is intentionally omitted\n# here to avoid conflicting duplicates.\n[[headers]]\n for = \"/*\"\n [headers.values]\n Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n X-Content-Type-Options = \"nosniff\"\n X-Frame-Options = \"SAMEORIGIN\"\n Referrer-Policy = \"strict-origin-when-cross-origin\"\n Permissions-Policy = \"camera=(), microphone=(), geolocation=(), interest-cohort=()\"\n Cross-Origin-Opener-Policy = \"same-origin\"\n\n[[headers]]\n for = \"/favicon.png\"\n [headers.values]\n Cache-Control = \"public, max-age=31536000, immutable\"\n\n[[headers]]\n for = \"/_app/immutable/*\"\n [headers.values]\n Cache-Control = \"public, max-age=31536000, immutable\"\n`,\n};\n\nexport const ALL_TEMPLATES: ConfigTemplate[] = [\n eslint,\n prettier,\n prettierIgnore,\n lighthouse,\n playwrightA11y,\n svelte,\n renovateAction,\n renovateConfig,\n netlify,\n];\n\nexport function templatesByName(which: ConfigName[]): ConfigTemplate[] {\n return ALL_TEMPLATES.filter((t) => which.includes(t.config));\n}\n"],"mappings":";AAQA,IAAM,SAAyB;AAAA,EAC7B,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAKZ;AAEA,IAAM,WAA2B;AAAA,EAC/B,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOZ;AAEA,IAAM,iBAAiC;AAAA,EACrC,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAMZ;AAEA,IAAM,aAA6B;AAAA,EACjC,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU,GAAG,KAAK;AAAA,IAChB;AAAA,MACE,OACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA;AAEH;AAEA,IAAM,iBAAiC;AAAA,EACrC,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAEZ;AAEA,IAAM,SAAyB;AAAA,EAC7B,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQZ;AAcA,IAAM,iBAAiC;AAAA,EACrC,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6CZ;AAEA,IAAM,iBAAiC;AAAA,EACrC,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAKZ;AAEA,IAAM,UAA0B;AAAA,EAC9B,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCZ;AAEO,IAAM,gBAAkC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,gBAAgB,OAAuC;AACrE,SAAO,cAAc,OAAO,CAAC,MAAM,MAAM,SAAS,EAAE,MAAM,CAAC;AAC7D;","names":[]}
@@ -0,0 +1,70 @@
1
+ // src/forms/client.ts
2
+ var INGEST_TIMEOUT_MS = 2e4;
3
+ async function submitToIngest(opts) {
4
+ const doFetch = opts.fetch ?? fetch;
5
+ const controller = new AbortController();
6
+ const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? INGEST_TIMEOUT_MS);
7
+ let res;
8
+ try {
9
+ res = await doFetch(opts.url, {
10
+ method: "POST",
11
+ headers: { "content-type": "application/json", "x-forms-token": opts.token },
12
+ body: JSON.stringify(opts.payload),
13
+ signal: controller.signal
14
+ });
15
+ } catch (err) {
16
+ return { ok: false, status: 0, error: `network error: ${String(err)}` };
17
+ } finally {
18
+ clearTimeout(timer);
19
+ }
20
+ let body = null;
21
+ try {
22
+ body = await res.json();
23
+ } catch {
24
+ }
25
+ const obj = body && typeof body === "object" ? body : null;
26
+ if (res.ok && obj && obj.ok === true) {
27
+ return { ok: true, id: String(obj.id ?? "") };
28
+ }
29
+ const error = obj && typeof obj.error === "string" ? obj.error : `ingest failed (${res.status})`;
30
+ return { ok: false, status: res.status, error };
31
+ }
32
+ async function submitScreenOut(opts) {
33
+ const doFetch = opts.fetch ?? fetch;
34
+ const controller = new AbortController();
35
+ const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1500);
36
+ try {
37
+ const res = await doFetch(opts.url, {
38
+ method: "POST",
39
+ headers: { "content-type": "application/json", "x-forms-token": opts.token },
40
+ // `_screenOut` is a reserved wire key (underscore-namespaced like `_meta`)
41
+ // so a site's real form field named "screenOut" can never read as a beacon.
42
+ body: JSON.stringify({ _screenOut: opts.reason }),
43
+ signal: controller.signal
44
+ });
45
+ return { ok: res.ok, status: res.status };
46
+ } catch {
47
+ return { ok: false, status: 0 };
48
+ } finally {
49
+ clearTimeout(timer);
50
+ }
51
+ }
52
+ var MIN_FILL_MS = 800;
53
+ function screenSubmission(input) {
54
+ if (typeof input.botField === "string" && input.botField.trim().length > 0) {
55
+ return { ok: false, reason: "honeypot" };
56
+ }
57
+ if (typeof input.elapsedMs === "number" && input.elapsedMs < MIN_FILL_MS) {
58
+ return { ok: false, reason: "too-fast" };
59
+ }
60
+ return { ok: true };
61
+ }
62
+
63
+ export {
64
+ INGEST_TIMEOUT_MS,
65
+ submitToIngest,
66
+ submitScreenOut,
67
+ MIN_FILL_MS,
68
+ screenSubmission
69
+ };
70
+ //# sourceMappingURL=chunk-A3WDWNN5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/forms/client.ts"],"sourcesContent":["import { SUBMISSION_FORM_TYPES, type FormType } from \"./types.js\";\nimport type { SubmissionMeta } from \"./meta.js\";\n\n/**\n * The JSON a fleet site forwards to the dashboard ingest endpoint. Typed fields\n * are optional; the index signature lets a site include its own extra fields\n * (e.g. `company`) which the dashboard normalizer captures into `extraFields`.\n *\n * Each typed field allows `string | undefined` (not just `string`) so a\n * `buildPayload` mapping can use the idiomatic `form.get(\"name\")?.toString()`\n * pattern under `exactOptionalPropertyTypes` without a cast — an absent field\n * and an explicit `undefined` both serialize away in the JSON body.\n */\nexport type SubmissionPayload = {\n formType?: FormType | string | undefined;\n name?: string | undefined;\n firstName?: string | undefined;\n lastName?: string | undefined;\n email?: string | undefined;\n phone?: string | undefined;\n message?: string | undefined;\n sourceUrl?: string | undefined;\n utm?: string | undefined;\n /** Reserved transport envelope (token/IP/UA); stripped centrally, never persisted. */\n _meta?: SubmissionMeta | undefined;\n [key: string]: unknown;\n};\n\nexport type IngestClientResult =\n { ok: true; id: string } | { ok: false; status: number; error: string };\n\nexport type SubmitToIngestOptions = {\n /** Full ingest endpoint incl. the site slug, e.g. https://…/api/forms/reddoor */\n url: string;\n /** The shared FORMS_INGEST_TOKEN. */\n token: string;\n payload: SubmissionPayload;\n /** Injectable fetch (pass SvelteKit's `event.fetch`); defaults to global fetch. */\n fetch?: typeof fetch;\n /** Abort budget for the central call. Default INGEST_TIMEOUT_MS. */\n timeoutMs?: number;\n};\n\n/** Default abort budget for the site→central ingest call. A central function hung\n * mid-deploy otherwise leaves the visitor's submit awaiting until Netlify kills\n * the SITE function at its sync limit — a broken response instead of the\n * friendly error copy (espada's 2026-07-10 form-e2e warns caught exactly this).\n *\n * ABORTING TOO EARLY IS THE WORSE FAILURE, because central persists the lead\n * BEFORE its best-effort tail (notify → stamp → fan-out, see ingest.ts): once\n * the row exists the lead is captured, so a client-side abort reports failure\n * for a submission that is already saved AND emailed. Observed on 1836dig\n * 2026-08-03: row `sub_f4f195ff` stored, operator email delivered, and the\n * visitor still got the failure copy — the visitor's only signal says the\n * opposite of the truth, and a retry duplicates the lead.\n *\n * The old 8s was calibrated against a 10s Netlify sync limit; the envelope is\n * now 30s (`SYNCHRONOUS_FUNCTION_TIMEOUT`), so that headroom argument no\n * longer binds. It also did not clear a COLD central call: measured\n * 2026-08-03, cold start ~1.9s + Airtable slug lookup ~2.4s + Turso\n * open/migrate + insert + Resend ~0.8s lands at ~5-7s, leaving 8s with no real\n * margin. 20s is ~3x that path and still leaves 10s of the envelope for the\n * action to render. A visitor waiting is recoverable; a visitor wrongly told\n * their message was lost is not. */\nexport const INGEST_TIMEOUT_MS = 20_000;\n\n/**\n * Forward a submission to the dashboard ingest endpoint. Never throws — a network\n * failure or a non-2xx response is returned as `{ ok: false }` so the caller can\n * show a friendly error rather than a 500.\n */\nexport async function submitToIngest(opts: SubmitToIngestOptions): Promise<IngestClientResult> {\n const doFetch = opts.fetch ?? fetch;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? INGEST_TIMEOUT_MS);\n let res: Response;\n try {\n res = await doFetch(opts.url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"x-forms-token\": opts.token },\n body: JSON.stringify(opts.payload),\n signal: controller.signal,\n });\n } catch (err) {\n return { ok: false, status: 0, error: `network error: ${String(err)}` };\n } finally {\n clearTimeout(timer);\n }\n let body: unknown = null;\n try {\n body = await res.json();\n } catch {\n // non-JSON response — fall through to the error path\n }\n const obj = body && typeof body === \"object\" ? (body as Record<string, unknown>) : null;\n if (res.ok && obj && obj.ok === true) {\n return { ok: true, id: String(obj.id ?? \"\") };\n }\n const error = obj && typeof obj.error === \"string\" ? obj.error : `ingest failed (${res.status})`;\n return { ok: false, status: res.status, error };\n}\n\nexport type SubmitScreenOutOptions = {\n /** Same ingest endpoint the site already posts submissions to. */\n url: string;\n token: string;\n reason: \"honeypot\" | \"too-fast\";\n fetch?: typeof fetch;\n /** Abort budget so a slow/hung beacon can't delay the (already-successful) response. */\n timeoutMs?: number;\n};\n\n/**\n * Best-effort screen-out beacon: tells the central ingest \"a bot was screened here\"\n * (no PII) so caught-vs-delivered is observable. Never throws — a failure is returned\n * as { ok: false } and the caller ignores it (the visitor already saw success).\n */\nexport async function submitScreenOut(\n opts: SubmitScreenOutOptions,\n): Promise<{ ok: boolean; status: number }> {\n const doFetch = opts.fetch ?? fetch;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1500);\n try {\n const res = await doFetch(opts.url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"x-forms-token\": opts.token },\n // `_screenOut` is a reserved wire key (underscore-namespaced like `_meta`)\n // so a site's real form field named \"screenOut\" can never read as a beacon.\n body: JSON.stringify({ _screenOut: opts.reason }),\n signal: controller.signal,\n });\n return { ok: res.ok, status: res.status };\n } catch {\n return { ok: false, status: 0 };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport type ScreenInput = { botField?: string | null; elapsedMs?: number | null };\nexport type ScreenResult = { ok: true } | { ok: false; reason: \"honeypot\" | \"too-fast\" };\n\n/**\n * Minimum plausible fill time; faster than this reads as a bot. Kept low (800ms)\n * on purpose: a too-fast fill is dropped *silently* (the visitor sees success),\n * so a real human who happens to be quick — autofill, a short form, a returning\n * visitor — would lose their lead with no trace. Below this, a submit is\n * effectively instant (page render → fill → click → network all under ~0.8s),\n * which a human realistically never beats but a script does. The honeypot is the\n * primary bot signal; this is the secondary one, so it errs toward letting\n * borderline-fast humans through.\n */\nexport const MIN_FILL_MS = 800;\n\n/**\n * Cheap bot screen for the site action. A filled honeypot is a bot; a submission\n * faster than MIN_FILL_MS is a bot. Missing timing data (null/undefined) is NOT a\n * rejection — a prerendered/cached page can't plant a fresh timestamp, and the\n * honeypot remains the primary signal.\n *\n * Any numeric elapsed BELOW the floor — including a NEGATIVE value — is too-fast. A\n * negative elapsed time is impossible for a real fill: it means the client posted a\n * FUTURE timestamp. The earlier `>= 0` guard let that slip through (negative skipped\n * the `< MIN_FILL_MS` branch and returned ok), silently bypassing the timing gate.\n */\nexport function screenSubmission(input: ScreenInput): ScreenResult {\n if (typeof input.botField === \"string\" && input.botField.trim().length > 0) {\n return { ok: false, reason: \"honeypot\" };\n }\n if (typeof input.elapsedMs === \"number\" && input.elapsedMs < MIN_FILL_MS) {\n return { ok: false, reason: \"too-fast\" };\n }\n return { ok: true };\n}\n\nexport { SUBMISSION_FORM_TYPES, type FormType };\n"],"mappings":";AAgEO,IAAM,oBAAoB;AAOjC,eAAsB,eAAe,MAA0D;AAC7F,QAAM,UAAU,KAAK,SAAS;AAC9B,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,aAAa,iBAAiB;AACtF,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,QAAQ,KAAK,KAAK;AAAA,MAC5B,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,iBAAiB,KAAK,MAAM;AAAA,MAC3E,MAAM,KAAK,UAAU,KAAK,OAAO;AAAA,MACjC,QAAQ,WAAW;AAAA,IACrB,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,OAAO,kBAAkB,OAAO,GAAG,CAAC,GAAG;AAAA,EACxE,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AACA,MAAI,OAAgB;AACpB,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AAAA,EAER;AACA,QAAM,MAAM,QAAQ,OAAO,SAAS,WAAY,OAAmC;AACnF,MAAI,IAAI,MAAM,OAAO,IAAI,OAAO,MAAM;AACpC,WAAO,EAAE,IAAI,MAAM,IAAI,OAAO,IAAI,MAAM,EAAE,EAAE;AAAA,EAC9C;AACA,QAAM,QAAQ,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,kBAAkB,IAAI,MAAM;AAC7F,SAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,QAAQ,MAAM;AAChD;AAiBA,eAAsB,gBACpB,MAC0C;AAC1C,QAAM,UAAU,KAAK,SAAS;AAC9B,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,aAAa,IAAI;AACzE,MAAI;AACF,UAAM,MAAM,MAAM,QAAQ,KAAK,KAAK;AAAA,MAClC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,iBAAiB,KAAK,MAAM;AAAA;AAAA;AAAA,MAG3E,MAAM,KAAK,UAAU,EAAE,YAAY,KAAK,OAAO,CAAC;AAAA,MAChD,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,WAAO,EAAE,IAAI,IAAI,IAAI,QAAQ,IAAI,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO,EAAE,IAAI,OAAO,QAAQ,EAAE;AAAA,EAChC,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AACF;AAeO,IAAM,cAAc;AAapB,SAAS,iBAAiB,OAAkC;AACjE,MAAI,OAAO,MAAM,aAAa,YAAY,MAAM,SAAS,KAAK,EAAE,SAAS,GAAG;AAC1E,WAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAAA,EACzC;AACA,MAAI,OAAO,MAAM,cAAc,YAAY,MAAM,YAAY,aAAa;AACxE,WAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAAA,EACzC;AACA,SAAO,EAAE,IAAI,KAAK;AACpB;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  withRecipe
3
- } from "./chunk-XKOZUVK2.js";
3
+ } from "./chunk-ZTPPCHDB.js";
4
4
  import {
5
5
  defaultSpawn
6
6
  } from "./chunk-HVOOCK6L.js";
@@ -76,4 +76,4 @@ async function bumpDeps(site, opts = {}) {
76
76
  export {
77
77
  bumpDeps
78
78
  };
79
- //# sourceMappingURL=chunk-D3DQFC32.js.map
79
+ //# sourceMappingURL=chunk-ATJ6ZA2V.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-J7SZQUCW.js";
4
4
  import {
5
5
  withRecipe
6
- } from "./chunk-XKOZUVK2.js";
6
+ } from "./chunk-ZTPPCHDB.js";
7
7
  import {
8
8
  bumpDep,
9
9
  readPackageJson,
@@ -243,4 +243,4 @@ async function upgradeSvelte4to5(site, opts = {}) {
243
243
  export {
244
244
  upgradeSvelte4to5
245
245
  };
246
- //# sourceMappingURL=chunk-ZO7WNY57.js.map
246
+ //# sourceMappingURL=chunk-B5ZUQZFL.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runMigrations
3
- } from "./chunk-HIN3P4ZN.js";
3
+ } from "./chunk-4SOCPK7A.js";
4
4
  import {
5
5
  defaultCredentialsPath
6
6
  } from "./chunk-6VAL7XJT.js";
@@ -48,4 +48,4 @@ export {
48
48
  readDbConfig,
49
49
  openDb
50
50
  };
51
- //# sourceMappingURL=chunk-PH3AGF4Z.js.map
51
+ //# sourceMappingURL=chunk-DC5H7KEQ.js.map
@@ -1,27 +1,27 @@
1
1
  import {
2
2
  convertToPnpm
3
- } from "./chunk-2X322ZES.js";
3
+ } from "./chunk-MGJ72BA4.js";
4
4
  import {
5
5
  svelteCodemods
6
- } from "./chunk-BTEBRTW4.js";
6
+ } from "./chunk-LS6QPANO.js";
7
7
  import {
8
8
  healthEndpoint
9
- } from "./chunk-7MJBTV2R.js";
9
+ } from "./chunk-FX7RAMGC.js";
10
10
  import {
11
11
  smokeSuite
12
- } from "./chunk-PKVZMFEB.js";
12
+ } from "./chunk-K5JZ5KLO.js";
13
13
  import {
14
14
  onboard
15
- } from "./chunk-ZYBOT5PM.js";
15
+ } from "./chunk-NT4WIP7I.js";
16
16
  import {
17
17
  syncConfigs
18
- } from "./chunk-JNXCDUYB.js";
18
+ } from "./chunk-Q276MZ7E.js";
19
19
  import {
20
20
  withRecipe
21
- } from "./chunk-XKOZUVK2.js";
21
+ } from "./chunk-ZTPPCHDB.js";
22
22
  import {
23
23
  runAudits
24
- } from "./chunk-3U3IGVAQ.js";
24
+ } from "./chunk-3LWKIKZ2.js";
25
25
  import {
26
26
  siteLabel
27
27
  } from "./chunk-XXTZBPUY.js";
@@ -143,4 +143,4 @@ export {
143
143
  DEFAULT_INIT_STEPS,
144
144
  init
145
145
  };
146
- //# sourceMappingURL=chunk-DYKG53IR.js.map
146
+ //# sourceMappingURL=chunk-FAQIMC26.js.map
@@ -20,7 +20,7 @@ import {
20
20
  } from "./chunk-WEKL2HYJ.js";
21
21
  import {
22
22
  recordFleetEventsBestEffort
23
- } from "./chunk-GYNRZCM6.js";
23
+ } from "./chunk-QG6OSL4M.js";
24
24
  import {
25
25
  listWebsites,
26
26
  siteSlug,
@@ -333,4 +333,4 @@ export {
333
333
  parseAddresses,
334
334
  isProbablyEmail
335
335
  };
336
- //# sourceMappingURL=chunk-4VAUSRSS.js.map
336
+ //# sourceMappingURL=chunk-FVVGDIGX.js.map
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-KRDLHAKK.js";
5
5
  import {
6
6
  withRecipe
7
- } from "./chunk-XKOZUVK2.js";
7
+ } from "./chunk-ZTPPCHDB.js";
8
8
  import {
9
9
  defaultSpawn
10
10
  } from "./chunk-HVOOCK6L.js";
@@ -142,4 +142,4 @@ async function healthEndpoint(site, deps = { spawn: defaultSpawn }) {
142
142
  export {
143
143
  healthEndpoint
144
144
  };
145
- //# sourceMappingURL=chunk-7MJBTV2R.js.map
145
+ //# sourceMappingURL=chunk-FX7RAMGC.js.map
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-KRDLHAKK.js";
5
5
  import {
6
6
  withRecipe
7
- } from "./chunk-XKOZUVK2.js";
7
+ } from "./chunk-ZTPPCHDB.js";
8
8
  import {
9
9
  defaultSpawn
10
10
  } from "./chunk-HVOOCK6L.js";
@@ -312,4 +312,4 @@ async function smokeSuite(site, deps = { spawn: defaultSpawn }) {
312
312
  export {
313
313
  smokeSuite
314
314
  };
315
- //# sourceMappingURL=chunk-PKVZMFEB.js.map
315
+ //# sourceMappingURL=chunk-K5JZ5KLO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  approveBlockers
3
- } from "./chunk-JTRTG5CU.js";
3
+ } from "./chunk-YIQ7IWE3.js";
4
4
  import {
5
5
  siteSlug
6
6
  } from "./chunk-GXVX4HUH.js";
@@ -297,4 +297,4 @@ export {
297
297
  readDigestState,
298
298
  writeDigestState
299
299
  };
300
- //# sourceMappingURL=chunk-CZAMYVOP.js.map
300
+ //# sourceMappingURL=chunk-KPQXB5CR.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-J7SZQUCW.js";
4
4
  import {
5
5
  withRecipe
6
- } from "./chunk-XKOZUVK2.js";
6
+ } from "./chunk-ZTPPCHDB.js";
7
7
 
8
8
  // src/recipes/svelte-codemods.ts
9
9
  import { writeFile } from "fs/promises";
@@ -32,4 +32,4 @@ async function svelteCodemods(site) {
32
32
  export {
33
33
  svelteCodemods
34
34
  };
35
- //# sourceMappingURL=chunk-BTEBRTW4.js.map
35
+ //# sourceMappingURL=chunk-LS6QPANO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  withRecipe
3
- } from "./chunk-XKOZUVK2.js";
3
+ } from "./chunk-ZTPPCHDB.js";
4
4
  import {
5
5
  readPackageJson,
6
6
  writePackageJson
@@ -96,4 +96,4 @@ async function convertToPnpm(site, opts = {}) {
96
96
  export {
97
97
  convertToPnpm
98
98
  };
99
- //# sourceMappingURL=chunk-2X322ZES.js.map
99
+ //# sourceMappingURL=chunk-MGJ72BA4.js.map
@@ -0,0 +1,137 @@
1
+ // src/github/rulesets.ts
2
+ var FLEET_RULESET_NAME = "main: reviewed changes only";
3
+ var DEFAULT_PULL_REQUEST_PARAMS = {
4
+ required_approving_review_count: 0,
5
+ dismiss_stale_reviews_on_push: false,
6
+ require_code_owner_review: false,
7
+ require_last_push_approval: false,
8
+ required_review_thread_resolution: false,
9
+ allowed_merge_methods: ["squash", "merge", "rebase"]
10
+ };
11
+ function coversDefaultBranch(include) {
12
+ return (include ?? []).some((r) => r === "~DEFAULT_BRANCH" || r === "~ALL");
13
+ }
14
+ function statusChecksOf(rule) {
15
+ const raw = rule?.parameters?.["required_status_checks"];
16
+ return Array.isArray(raw) ? raw : [];
17
+ }
18
+ function ruleOfType(rules, type) {
19
+ return (rules ?? []).find((r) => r.type === type);
20
+ }
21
+ function desiredRuleset(requiredCheck) {
22
+ return {
23
+ name: FLEET_RULESET_NAME,
24
+ target: "branch",
25
+ enforcement: "active",
26
+ bypass_actors: [],
27
+ conditions: { ref_name: { include: ["~DEFAULT_BRANCH"], exclude: [] } },
28
+ rules: [
29
+ { type: "deletion" },
30
+ { type: "non_fast_forward" },
31
+ { type: "pull_request", parameters: { ...DEFAULT_PULL_REQUEST_PARAMS } },
32
+ ...requiredCheck === null ? [] : [
33
+ {
34
+ type: "required_status_checks",
35
+ parameters: {
36
+ strict_required_status_checks_policy: true,
37
+ do_not_enforce_on_create: false,
38
+ required_status_checks: [{ context: requiredCheck }]
39
+ }
40
+ }
41
+ ]
42
+ ]
43
+ };
44
+ }
45
+ function rulesetGaps(existing, requiredCheck) {
46
+ const gaps = [];
47
+ if (existing.enforcement !== "active") {
48
+ gaps.push(`enforcement is ${JSON.stringify(existing.enforcement ?? "unset")}, not "active"`);
49
+ }
50
+ const bypass = existing.bypass_actors ?? [];
51
+ if (bypass.length > 0) {
52
+ gaps.push(`${bypass.length} bypass actor(s) present (fleet floor is an empty bypass list)`);
53
+ }
54
+ if (!coversDefaultBranch(existing.conditions?.ref_name?.include)) {
55
+ gaps.push("ref conditions do not cover the default branch");
56
+ }
57
+ const exclude = existing.conditions?.ref_name?.exclude ?? [];
58
+ if (exclude.length > 0) {
59
+ gaps.push(
60
+ `${exclude.length} ref exclude pattern(s) present (excludes can only weaken; fleet floor is none)`
61
+ );
62
+ }
63
+ for (const type of ["deletion", "non_fast_forward", "pull_request"]) {
64
+ if (!ruleOfType(existing.rules, type)) gaps.push(`missing ${type} rule`);
65
+ }
66
+ if (requiredCheck !== null) {
67
+ const rsc = ruleOfType(existing.rules, "required_status_checks");
68
+ if (!rsc) {
69
+ gaps.push(`missing required_status_checks rule ("${requiredCheck}" is observed on the repo)`);
70
+ } else {
71
+ if (!statusChecksOf(rsc).some((c) => c.context === requiredCheck)) {
72
+ gaps.push(`"${requiredCheck}" not among the required status checks`);
73
+ }
74
+ if (rsc.parameters?.["strict_required_status_checks_policy"] !== true) {
75
+ gaps.push("strict (branch-up-to-date) status-check policy is off");
76
+ }
77
+ }
78
+ }
79
+ return gaps;
80
+ }
81
+ function healRuleset(existing, requiredCheck) {
82
+ const rules = (existing.rules ?? []).map((r) => ({
83
+ type: r.type,
84
+ ...r.parameters === void 0 ? {} : { parameters: { ...r.parameters } }
85
+ }));
86
+ for (const type of ["deletion", "non_fast_forward"]) {
87
+ if (!ruleOfType(rules, type)) rules.push({ type });
88
+ }
89
+ if (!ruleOfType(rules, "pull_request")) {
90
+ rules.push({ type: "pull_request", parameters: { ...DEFAULT_PULL_REQUEST_PARAMS } });
91
+ }
92
+ if (requiredCheck !== null) {
93
+ const rsc = ruleOfType(rules, "required_status_checks");
94
+ if (!rsc) {
95
+ rules.push({
96
+ type: "required_status_checks",
97
+ parameters: {
98
+ strict_required_status_checks_policy: true,
99
+ do_not_enforce_on_create: false,
100
+ required_status_checks: [{ context: requiredCheck }]
101
+ }
102
+ });
103
+ } else {
104
+ const params = rsc.parameters ??= {};
105
+ const checks = statusChecksOf(rsc);
106
+ if (!checks.some((c) => c.context === requiredCheck)) {
107
+ params["required_status_checks"] = [...checks, { context: requiredCheck }];
108
+ }
109
+ params["strict_required_status_checks_policy"] = true;
110
+ }
111
+ }
112
+ const include = existing.conditions?.ref_name?.include ?? [];
113
+ return {
114
+ name: existing.name,
115
+ target: "branch",
116
+ enforcement: "active",
117
+ bypass_actors: [],
118
+ conditions: {
119
+ ref_name: {
120
+ include: coversDefaultBranch(include) ? [...include] : [...include, "~DEFAULT_BRANCH"],
121
+ // Excludes are cleared, never preserved: they can only remove
122
+ // protection, and the pure logic cannot tell a benign pattern from
123
+ // one that neutralizes the default branch (see rulesetGaps).
124
+ exclude: []
125
+ }
126
+ },
127
+ rules
128
+ };
129
+ }
130
+
131
+ export {
132
+ FLEET_RULESET_NAME,
133
+ desiredRuleset,
134
+ rulesetGaps,
135
+ healRuleset
136
+ };
137
+ //# sourceMappingURL=chunk-MNDIED4M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/github/rulesets.ts"],"sourcesContent":["/**\n * The pure invariant core of ruleset self-healing (spec:\n * docs/superpowers/specs/2026-08-02-ruleset-self-healing-design.md).\n *\n * Everything here is pure data-in/data-out so the brick-risk logic — \"when is\n * it safe to require a status check\", \"what does healing preserve\" — is unit\n * tested directly. The API I/O lives in gh.ts as thin wrappers.\n *\n * Why rulesets at all, when `protectBranch` (classic protection) already\n * exists: classic protection's `enforce_admins: false` exempts admins, and on\n * 2026-07-26 that hole let a bulk `gh pr merge --auto` sweep land two\n * unreviewed MAJOR bumps (reddoor-starter#77, reddoor-website#111). A ruleset\n * with an EMPTY bypass list binds everyone, including admins. Classic\n * protection stays in place (union-enforced) — this is the stronger layer on\n * top, and the one the fleet actually relies on.\n */\n\n/** One rule inside a ruleset. Kept structurally loose (`parameters` is an open\n * record) so a GitHub-added rule type or parameter can never crash the parse —\n * unknown shapes flow through healing untouched. */\nexport type RulesetRule = {\n type: string;\n parameters?: Record<string, unknown>;\n};\n\n/** The subset of a GET /repos/{repo}/rulesets/{id} response the logic reads.\n * Extra fields (timestamps, node_id, _links, current_user_can_bypass) are\n * ignored on read and never echoed back on PUT. */\nexport type ExistingRuleset = {\n id: number;\n name: string;\n enforcement?: string;\n bypass_actors?: unknown[];\n conditions?: { ref_name?: { include?: string[]; exclude?: string[] } };\n rules?: RulesetRule[];\n};\n\n/** The payload shape POST/PUT accepts. */\nexport type RulesetPayload = {\n name: string;\n target: \"branch\";\n enforcement: \"active\";\n bypass_actors: unknown[];\n conditions: { ref_name: { include: string[]; exclude: string[] } };\n rules: RulesetRule[];\n};\n\n/** Canonical fleet ruleset name — matches the 22 hand-applied 2026-07-31/08-01. */\nexport const FLEET_RULESET_NAME = \"main: reviewed changes only\";\n\n/** `pull_request` defaults for a CREATED ruleset. 0 approvals is deliberate:\n * one-member org + GitHub forbids self-approval, so any required approval\n * deadlocks every PR (operator- and Renovate-authored alike) until the\n * Renovate App identity exists. On HEAL these are never imposed — existing\n * parameters are preserved so a hand-hardened repo (e.g.\n * `required_review_thread_resolution: true`) is not fought back down. */\nconst DEFAULT_PULL_REQUEST_PARAMS: Record<string, unknown> = {\n required_approving_review_count: 0,\n dismiss_stale_reviews_on_push: false,\n require_code_owner_review: false,\n require_last_push_approval: false,\n required_review_thread_resolution: false,\n allowed_merge_methods: [\"squash\", \"merge\", \"rebase\"],\n};\n\n/** The ref-condition tokens that cover the default branch. `~ALL` is a\n * superset of `~DEFAULT_BRANCH`, so either satisfies the floor. */\nfunction coversDefaultBranch(include: string[] | undefined): boolean {\n return (include ?? []).some((r) => r === \"~DEFAULT_BRANCH\" || r === \"~ALL\");\n}\n\nfunction statusChecksOf(rule: RulesetRule | undefined): Array<{ context?: string }> {\n const raw = rule?.parameters?.[\"required_status_checks\"];\n return Array.isArray(raw) ? (raw as Array<{ context?: string }>) : [];\n}\n\nfunction ruleOfType(rules: RulesetRule[] | undefined, type: string): RulesetRule | undefined {\n return (rules ?? []).find((r) => r.type === type);\n}\n\n/**\n * The desired payload for a repo with NO existing fleet ruleset.\n *\n * `requiredCheck` is the two-stage gate: pass the context string ONLY when\n * that check-run has been observed on the default branch (see\n * `checkContextObserved` in gh.ts); pass `null` otherwise and the ruleset is\n * created with the refs rules alone. Requiring a context that never fires\n * would make the repo permanently unmergeable by EVERYONE — bypass_actors is\n * empty by design, so there is no admin escape. (`.github` sits at stage 1\n * forever for exactly this reason: its ci.yml is `workflow_call`-only and\n * emits no check runs.)\n */\nexport function desiredRuleset(requiredCheck: string | null): RulesetPayload {\n return {\n name: FLEET_RULESET_NAME,\n target: \"branch\",\n enforcement: \"active\",\n bypass_actors: [],\n conditions: { ref_name: { include: [\"~DEFAULT_BRANCH\"], exclude: [] } },\n rules: [\n { type: \"deletion\" },\n { type: \"non_fast_forward\" },\n { type: \"pull_request\", parameters: { ...DEFAULT_PULL_REQUEST_PARAMS } },\n ...(requiredCheck === null\n ? []\n : [\n {\n type: \"required_status_checks\",\n parameters: {\n strict_required_status_checks_policy: true,\n do_not_enforce_on_create: false,\n required_status_checks: [{ context: requiredCheck }],\n },\n },\n ]),\n ],\n };\n}\n\n/**\n * Human-readable floor violations of an existing ruleset; empty array = the\n * ruleset satisfies the fleet floor and must NOT be touched (the noop path).\n *\n * The floor is deliberately a MINIMUM, not an exact shape: extra rules and\n * stronger `pull_request` parameters are not gaps. The one aggressive check is\n * `bypass_actors` — ANY entry is drift, because an empty bypass list is the\n * entire point of the ruleset layer (it is what binds admins). When the\n * Renovate App identity someday warrants a bypass actor, this is the single\n * place that decision gets encoded.\n *\n * `requiredCheck: null` scopes the floor to stage 1 (refs rules only); a\n * string additionally demands that context among required status checks with\n * strict (up-to-date) policy.\n */\nexport function rulesetGaps(existing: ExistingRuleset, requiredCheck: string | null): string[] {\n const gaps: string[] = [];\n if (existing.enforcement !== \"active\") {\n gaps.push(`enforcement is ${JSON.stringify(existing.enforcement ?? \"unset\")}, not \"active\"`);\n }\n const bypass = existing.bypass_actors ?? [];\n if (bypass.length > 0) {\n gaps.push(`${bypass.length} bypass actor(s) present (fleet floor is an empty bypass list)`);\n }\n if (!coversDefaultBranch(existing.conditions?.ref_name?.include)) {\n gaps.push(\"ref conditions do not cover the default branch\");\n }\n // Exclude patterns can ONLY remove protection (GitHub applies exclude over\n // include), and the pure floor cannot resolve which patterns match the\n // default branch — `exclude: [\"refs/heads/main\"]` neutralizes the entire\n // ruleset while every other check here still passes. It is also the\n // least-destructive-LOOKING admin edit (\"I'll just exclude main for a\n // minute\"), i.e. exactly the bypass drift this layer alarms on. The fleet\n // floor is therefore NO excludes at all.\n const exclude = existing.conditions?.ref_name?.exclude ?? [];\n if (exclude.length > 0) {\n gaps.push(\n `${exclude.length} ref exclude pattern(s) present (excludes can only weaken; fleet floor is none)`,\n );\n }\n for (const type of [\"deletion\", \"non_fast_forward\", \"pull_request\"]) {\n if (!ruleOfType(existing.rules, type)) gaps.push(`missing ${type} rule`);\n }\n if (requiredCheck !== null) {\n const rsc = ruleOfType(existing.rules, \"required_status_checks\");\n if (!rsc) {\n gaps.push(`missing required_status_checks rule (\"${requiredCheck}\" is observed on the repo)`);\n } else {\n if (!statusChecksOf(rsc).some((c) => c.context === requiredCheck)) {\n gaps.push(`\"${requiredCheck}\" not among the required status checks`);\n }\n if (rsc.parameters?.[\"strict_required_status_checks_policy\"] !== true) {\n gaps.push(\"strict (branch-up-to-date) status-check policy is off\");\n }\n }\n }\n return gaps;\n}\n\n/**\n * The healed PUT payload for a drifted ruleset: existing content merged TOWARD\n * the floor, never away from it.\n *\n * Preserved verbatim: extra rule types (a hand-added `required_linear_history`\n * survives), existing `pull_request` parameters (hand-hardening survives),\n * other required status-check contexts (union, never replace — the same\n * lesson the classic protectBranch PUT learned), and existing ref INCLUDES\n * beyond the default-branch guarantee (extra includes only strengthen).\n *\n * Healed: enforcement → active, bypass_actors → [], and ref excludes → []\n * (both deliberate exceptions to never-weaken, because both are pure\n * weakenings — a bypass actor or an exclude matching the default branch IS\n * the drift being alarmed on), missing rules added, and — only when\n * `requiredCheck` is non-null — the context unioned in with strict policy\n * forced on.\n */\nexport function healRuleset(\n existing: ExistingRuleset,\n requiredCheck: string | null,\n): RulesetPayload {\n const rules: RulesetRule[] = (existing.rules ?? []).map((r) => ({\n type: r.type,\n ...(r.parameters === undefined ? {} : { parameters: { ...r.parameters } }),\n }));\n for (const type of [\"deletion\", \"non_fast_forward\"]) {\n if (!ruleOfType(rules, type)) rules.push({ type });\n }\n if (!ruleOfType(rules, \"pull_request\")) {\n rules.push({ type: \"pull_request\", parameters: { ...DEFAULT_PULL_REQUEST_PARAMS } });\n }\n if (requiredCheck !== null) {\n const rsc = ruleOfType(rules, \"required_status_checks\");\n if (!rsc) {\n rules.push({\n type: \"required_status_checks\",\n parameters: {\n strict_required_status_checks_policy: true,\n do_not_enforce_on_create: false,\n required_status_checks: [{ context: requiredCheck }],\n },\n });\n } else {\n const params = (rsc.parameters ??= {});\n const checks = statusChecksOf(rsc);\n if (!checks.some((c) => c.context === requiredCheck)) {\n params[\"required_status_checks\"] = [...checks, { context: requiredCheck }];\n }\n params[\"strict_required_status_checks_policy\"] = true;\n }\n }\n\n const include = existing.conditions?.ref_name?.include ?? [];\n return {\n name: existing.name,\n target: \"branch\",\n enforcement: \"active\",\n bypass_actors: [],\n conditions: {\n ref_name: {\n include: coversDefaultBranch(include) ? [...include] : [...include, \"~DEFAULT_BRANCH\"],\n // Excludes are cleared, never preserved: they can only remove\n // protection, and the pure logic cannot tell a benign pattern from\n // one that neutralizes the default branch (see rulesetGaps).\n exclude: [],\n },\n },\n rules,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  withRecipe
3
- } from "./chunk-XKOZUVK2.js";
3
+ } from "./chunk-ZTPPCHDB.js";
4
4
  import {
5
5
  bumpDep,
6
6
  readPackageJson,
@@ -150,4 +150,4 @@ export {
150
150
  selfCaretRange,
151
151
  onboard
152
152
  };
153
- //# sourceMappingURL=chunk-ZYBOT5PM.js.map
153
+ //# sourceMappingURL=chunk-NT4WIP7I.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ALL_TEMPLATES,
3
3
  templatesByName
4
- } from "./chunk-CASCWQQX.js";
4
+ } from "./chunk-73UFRZKY.js";
5
5
  import {
6
6
  withRecipe
7
- } from "./chunk-XKOZUVK2.js";
7
+ } from "./chunk-ZTPPCHDB.js";
8
8
  import {
9
9
  listTrackedFiles,
10
10
  removeFromIndex
@@ -125,7 +125,6 @@ var ALL_CONFIG_NAMES = [
125
125
  "playwright-a11y",
126
126
  "svelte",
127
127
  "gitignore",
128
- "ci",
129
128
  "renovate-action",
130
129
  "renovate-config",
131
130
  "netlify"
@@ -208,4 +207,4 @@ export {
208
207
  isConfigName,
209
208
  syncConfigs
210
209
  };
211
- //# sourceMappingURL=chunk-JNXCDUYB.js.map
210
+ //# sourceMappingURL=chunk-Q276MZ7E.js.map