@runeditrun/rer 0.0.0-stage → 0.1.1

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 (527) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -2
  3. package/bin/rer.mjs +3 -0
  4. package/cli/README.md +243 -0
  5. package/cli/adapter.mjs +10 -0
  6. package/cli/ai-local.mjs +65 -0
  7. package/cli/archive-path.mjs +52 -0
  8. package/cli/archive-vite.mjs +55 -0
  9. package/cli/archive.mjs +8 -0
  10. package/cli/auth-copy.mjs +80 -0
  11. package/cli/bounded.mjs +70 -0
  12. package/cli/cache-doctor.mjs +63 -0
  13. package/cli/container-transfer.mjs +35 -0
  14. package/cli/container.mjs +62 -0
  15. package/cli/contract.mjs +25 -0
  16. package/cli/dependencies.mjs +45 -0
  17. package/cli/dependency-approval.mjs +33 -0
  18. package/cli/dependency-doctor.mjs +34 -0
  19. package/cli/dependency-input.mjs +43 -0
  20. package/cli/deploy-config.mjs +118 -0
  21. package/cli/deploy.mjs +37 -0
  22. package/cli/dev-identity.mjs +80 -0
  23. package/cli/errors.mjs +14 -0
  24. package/cli/export.mjs +42 -0
  25. package/cli/font-cache.mjs +29 -0
  26. package/cli/hosted-output.mjs +110 -0
  27. package/cli/independent.mjs +29 -0
  28. package/cli/inspect.mjs +174 -0
  29. package/cli/jsonc.mjs +111 -0
  30. package/cli/lifecycle.mjs +148 -0
  31. package/cli/local.mjs +100 -0
  32. package/cli/main.mjs +57 -0
  33. package/cli/managed-server.mjs +143 -0
  34. package/cli/node-runtime-cache.mjs +68 -0
  35. package/cli/node-runtime-discovery.mjs +56 -0
  36. package/cli/node-runtime-download.mjs +59 -0
  37. package/cli/node-runtime.mjs +50 -0
  38. package/cli/output.mjs +22 -0
  39. package/cli/parse.mjs +77 -0
  40. package/cli/port.mjs +52 -0
  41. package/cli/portability-worker.mjs +56 -0
  42. package/cli/portability.mjs +66 -0
  43. package/cli/preview-cache.mjs +99 -0
  44. package/cli/project.mjs +48 -0
  45. package/cli/release.mjs +24 -0
  46. package/cli/runtime-front-door.mjs +46 -0
  47. package/cli/scheduled.mjs +81 -0
  48. package/cli/server-activity.mjs +12 -0
  49. package/cli/server-proxy.mjs +93 -0
  50. package/cli/server-registry.mjs +182 -0
  51. package/cli/server-resources.mjs +22 -0
  52. package/cli/settings.mjs +15 -0
  53. package/cli/setup-answers.mjs +29 -0
  54. package/cli/setup-input.mjs +113 -0
  55. package/cli/shared.mjs +7 -0
  56. package/cli/transfers.mjs +33 -0
  57. package/cli/version.mjs +36 -0
  58. package/composition/git-source.mjs +111 -0
  59. package/edition-authoring/files.mjs +81 -0
  60. package/edition-authoring/manifest.mjs +65 -0
  61. package/global/README.md +158 -0
  62. package/global/checkout.mjs +72 -0
  63. package/global/cli/command.mjs +13 -0
  64. package/global/cli/install.mjs +75 -0
  65. package/global/cli/layout.mjs +45 -0
  66. package/global/cli/status.mjs +54 -0
  67. package/global/cli/uninstall.mjs +36 -0
  68. package/global/delegate.mjs +13 -0
  69. package/global/launch/capacity.mjs +72 -0
  70. package/global/launch/dev.mjs +117 -0
  71. package/global/launch/facts.mjs +94 -0
  72. package/global/launch/failure.mjs +5 -0
  73. package/global/launch/local-defaults.mjs +4 -0
  74. package/global/launch/prepare.mjs +63 -0
  75. package/global/launch/render.mjs +44 -0
  76. package/global/launch/state.mjs +106 -0
  77. package/global/launch/steps.mjs +90 -0
  78. package/global/launch/stop.mjs +89 -0
  79. package/global/launch/worker.mjs +62 -0
  80. package/global/list.mjs +59 -0
  81. package/global/main.mjs +53 -0
  82. package/global/package-info.mjs +38 -0
  83. package/global/registry.mjs +24 -0
  84. package/global/rer.mjs +29 -0
  85. package/global/update/archive.mjs +89 -0
  86. package/global/update/command.mjs +90 -0
  87. package/global/update/latest.mjs +6 -0
  88. package/global/update/notice.mjs +58 -0
  89. package/global/update/registry.mjs +47 -0
  90. package/global/version.mjs +29 -0
  91. package/install.sh +144 -0
  92. package/modules/ai/LICENSE +21 -0
  93. package/modules/ai/README.md +90 -0
  94. package/modules/ai/client.mjs +71 -0
  95. package/modules/ai/config.mjs +39 -0
  96. package/modules/ai/dev/index.d.mts +6 -0
  97. package/modules/ai/dev/index.mjs +64 -0
  98. package/modules/ai/dev/outbox.mjs +33 -0
  99. package/modules/ai/errors.mjs +28 -0
  100. package/modules/ai/fake/index.d.mts +3 -0
  101. package/modules/ai/fake/index.mjs +82 -0
  102. package/modules/ai/index.d.mts +10 -0
  103. package/modules/ai/index.mjs +3 -0
  104. package/modules/ai/package.json +6 -0
  105. package/modules/container/README.md +156 -0
  106. package/modules/container/adapters/d1.mjs +34 -0
  107. package/modules/container/adapters/egress.mjs +40 -0
  108. package/modules/container/adapters/jobs.mjs +47 -0
  109. package/modules/container/adapters/objects.mjs +84 -0
  110. package/modules/container/adapters/retained.mjs +14 -0
  111. package/modules/container/adapters/sqlite-error.mjs +7 -0
  112. package/modules/container/adapters/sqlite-worker.mjs +26 -0
  113. package/modules/container/assets.mjs +14 -0
  114. package/modules/container/bindings.mjs +1 -0
  115. package/modules/container/boundary.mjs +38 -0
  116. package/modules/container/build.mjs +65 -0
  117. package/modules/container/deploy-input.mjs +17 -0
  118. package/modules/container/entrypoint.sh +5 -0
  119. package/modules/container/host.mjs +43 -0
  120. package/modules/container/ingress.mjs +23 -0
  121. package/modules/container/lock.mjs +11 -0
  122. package/modules/container/migrations.mjs +16 -0
  123. package/modules/container/node-engine.mjs +39 -0
  124. package/modules/container/portability-schema.mjs +14 -0
  125. package/modules/container/remote/deploy.py +76 -0
  126. package/modules/container/remote/files.py +275 -0
  127. package/modules/container/remote/resources.py +157 -0
  128. package/modules/container/runner.mjs +10 -0
  129. package/modules/container/ssh-plan.mjs +44 -0
  130. package/modules/container/ssh.mjs +63 -0
  131. package/modules/container/transfer.mjs +27 -0
  132. package/modules/container/worker-engine.mjs +32 -0
  133. package/modules/dependencies/README.md +211 -0
  134. package/modules/dependencies/approval.mjs +51 -0
  135. package/modules/dependencies/auth.mjs +40 -0
  136. package/modules/dependencies/authority.mjs +14 -0
  137. package/modules/dependencies/crypto.mjs +13 -0
  138. package/modules/dependencies/index.d.mts +23 -0
  139. package/modules/dependencies/index.mjs +113 -0
  140. package/modules/dependencies/logout.mjs +42 -0
  141. package/modules/dependencies/native.mjs +9 -0
  142. package/modules/dependencies/protocol.mjs +23 -0
  143. package/modules/dependencies/schema/approval-attempt.sql +3 -0
  144. package/modules/dependencies/schema/dependencies.sql +15 -0
  145. package/modules/dependencies/store.mjs +19 -0
  146. package/modules/deploy-kit/README.md +308 -0
  147. package/modules/deploy-kit/artifact.mjs +47 -0
  148. package/modules/deploy-kit/build-modules.mjs +21 -0
  149. package/modules/deploy-kit/check.mjs +13 -0
  150. package/modules/deploy-kit/cli.mjs +50 -0
  151. package/modules/deploy-kit/container-config.mjs +8 -0
  152. package/modules/deploy-kit/default-stage.mjs +14 -0
  153. package/modules/deploy-kit/deploy.mjs +80 -0
  154. package/modules/deploy-kit/health-request.mjs +86 -0
  155. package/modules/deploy-kit/health.mjs +99 -0
  156. package/modules/deploy-kit/index.mjs +12 -0
  157. package/modules/deploy-kit/local-only.mjs +10 -0
  158. package/modules/deploy-kit/migrations.mjs +48 -0
  159. package/modules/deploy-kit/owner-cli.mjs +63 -0
  160. package/modules/deploy-kit/owner.mjs +297 -0
  161. package/modules/deploy-kit/prepare.mjs +62 -0
  162. package/modules/deploy-kit/provider.mjs +99 -0
  163. package/modules/deploy-kit/queue-consumer.mjs +10 -0
  164. package/modules/deploy-kit/receipt.mjs +16 -0
  165. package/modules/deploy-kit/render.mjs +47 -0
  166. package/modules/deploy-kit/resources.mjs +106 -0
  167. package/modules/deploy-kit/service-grants.mjs +25 -0
  168. package/modules/deploy-kit/sql-safety.mjs +94 -0
  169. package/modules/deploy-kit/sql-tokens.mjs +49 -0
  170. package/modules/deploy-kit/teardown.mjs +100 -0
  171. package/modules/deploy-kit/templates.mjs +31 -0
  172. package/modules/deploy-kit/validation.mjs +58 -0
  173. package/modules/deploy-kit/worker.mjs +105 -0
  174. package/modules/erasure/CONTRACT.md +90 -0
  175. package/modules/erasure/README.md +221 -0
  176. package/modules/erasure/audit.mjs +23 -0
  177. package/modules/erasure/blobs.mjs +45 -0
  178. package/modules/erasure/cascade.mjs +91 -0
  179. package/modules/erasure/completion.mjs +62 -0
  180. package/modules/erasure/declaration.mjs +51 -0
  181. package/modules/erasure/fence.mjs +19 -0
  182. package/modules/erasure/index.d.mts +62 -0
  183. package/modules/erasure/index.mjs +85 -0
  184. package/modules/erasure/order.mjs +56 -0
  185. package/modules/erasure/plan.mjs +121 -0
  186. package/modules/erasure/rate.mjs +57 -0
  187. package/modules/erasure/receipt.mjs +32 -0
  188. package/modules/erasure/root.mjs +21 -0
  189. package/modules/erasure/schema/erasure-rates.sql +5 -0
  190. package/modules/erasure/schema/erasure.sql +27 -0
  191. package/modules/erasure/scope.mjs +29 -0
  192. package/modules/erasure/sql.mjs +23 -0
  193. package/modules/erasure/verification.mjs +69 -0
  194. package/modules/integrations/core/CONTRACT.md +50 -0
  195. package/modules/integrations/core/README.md +27 -0
  196. package/modules/integrations/core/crypto.ts +19 -0
  197. package/modules/integrations/core/database.ts +21 -0
  198. package/modules/integrations/core/delivery.ts +97 -0
  199. package/modules/integrations/core/destination.ts +13 -0
  200. package/modules/integrations/core/index.ts +7 -0
  201. package/modules/integrations/core/node-transport.ts +72 -0
  202. package/modules/integrations/core/public-address.ts +57 -0
  203. package/modules/integrations/core/repository.ts +74 -0
  204. package/modules/integrations/core/schema.sql +36 -0
  205. package/modules/integrations/core/sqlite.ts +20 -0
  206. package/modules/integrations/core/timestamped-body-signature.ts +38 -0
  207. package/modules/integrations/core/tokens.ts +71 -0
  208. package/modules/integrations/core/types.ts +52 -0
  209. package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
  210. package/modules/integrations/core/webhook.ts +73 -0
  211. package/modules/job-queue/README.md +173 -0
  212. package/modules/job-queue/cli.mjs +64 -0
  213. package/modules/job-queue/command.mjs +64 -0
  214. package/modules/job-queue/config.mjs +48 -0
  215. package/modules/job-queue/identity.mjs +48 -0
  216. package/modules/job-queue/index.mjs +135 -0
  217. package/modules/job-queue/membership.mjs +30 -0
  218. package/modules/job-queue/module.json +10 -0
  219. package/modules/job-queue/policy.mjs +29 -0
  220. package/modules/job-queue/processes.mjs +80 -0
  221. package/modules/job-queue/resources.mjs +37 -0
  222. package/modules/job-queue/storage.mjs +64 -0
  223. package/modules/job-queue/supervisor.mjs +30 -0
  224. package/modules/job-queue/waiting.mjs +16 -0
  225. package/modules/mail/LICENSE +21 -0
  226. package/modules/mail/README.md +336 -0
  227. package/modules/mail/doctor.mjs +81 -0
  228. package/modules/mail/index.mjs +8 -0
  229. package/modules/mail/live-proof/README.md +113 -0
  230. package/modules/mail/live-proof/run.mjs +55 -0
  231. package/modules/mail/package.json +19 -0
  232. package/modules/mail/pnpm-lock.yaml +537 -0
  233. package/modules/mail/src/adapters/cloudflare/README.md +84 -0
  234. package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
  235. package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
  236. package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
  237. package/modules/mail/src/adapters/messaging/README.md +92 -0
  238. package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
  239. package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
  240. package/modules/mail/src/adapters/messaging/index.mjs +61 -0
  241. package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
  242. package/modules/mail/src/adapters/messaging/request.mjs +52 -0
  243. package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
  244. package/modules/mail/src/adapters/resend/README.md +120 -0
  245. package/modules/mail/src/adapters/resend/index.mjs +22 -0
  246. package/modules/mail/src/adapters/resend/response.mjs +54 -0
  247. package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
  248. package/modules/mail/src/adapters/resend/size.mjs +99 -0
  249. package/modules/mail/src/adapters/sendgrid/README.md +75 -0
  250. package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
  251. package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
  252. package/modules/mail/src/core/errors.mjs +9 -0
  253. package/modules/mail/src/core/transport.mjs +33 -0
  254. package/modules/mail/src/core/validation.mjs +172 -0
  255. package/modules/mail/src/development/index.d.mts +37 -0
  256. package/modules/mail/src/development/index.mjs +35 -0
  257. package/modules/mail/src/development/outbox.mjs +78 -0
  258. package/modules/mail/src/development/schema.sql +9 -0
  259. package/modules/mail/src/development/viewer-html.mjs +42 -0
  260. package/modules/mail/src/development/viewer.mjs +65 -0
  261. package/modules/mail/src/doctor/README.md +107 -0
  262. package/modules/mail/src/doctor/common.mjs +64 -0
  263. package/modules/mail/src/doctor/dns.mjs +102 -0
  264. package/modules/mail/src/doctor/index.mjs +28 -0
  265. package/modules/mail/src/doctor/probe.mjs +66 -0
  266. package/modules/mail/src/doctor/receive.mjs +81 -0
  267. package/modules/mail/src/doctor/records.mjs +45 -0
  268. package/modules/mail/src/events/README.md +268 -0
  269. package/modules/mail/src/events/d1-schema.sql +25 -0
  270. package/modules/mail/src/events/d1-storage.mjs +76 -0
  271. package/modules/mail/src/events/index.mjs +3 -0
  272. package/modules/mail/src/events/intake.mjs +201 -0
  273. package/modules/mail/src/events/storage.mjs +52 -0
  274. package/modules/mail/src/events/verify.mjs +92 -0
  275. package/modules/mail/src/inbound/README.md +137 -0
  276. package/modules/mail/src/inbound/bytes.mjs +91 -0
  277. package/modules/mail/src/inbound/headers.mjs +143 -0
  278. package/modules/mail/src/inbound/index.mjs +29 -0
  279. package/modules/mail/src/inbound/mime.mjs +36 -0
  280. package/modules/mail/src/inbound/multipart.mjs +27 -0
  281. package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
  282. package/modules/mail/src/inbound/resend.mjs +54 -0
  283. package/modules/mail/src/inbound/sendgrid.mjs +60 -0
  284. package/modules/mail/src/sender.mjs +33 -0
  285. package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
  286. package/modules/mail/src/sendgrid-email/index.mjs +86 -0
  287. package/modules/mail/src/unsubscribe/index.mjs +142 -0
  288. package/modules/operator-auth/README.md +599 -0
  289. package/modules/operator-auth/accounts/adapter.mjs +41 -0
  290. package/modules/operator-auth/accounts/admin.mjs +159 -0
  291. package/modules/operator-auth/accounts/credentials.mjs +27 -0
  292. package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
  293. package/modules/operator-auth/accounts/index.d.mts +9 -0
  294. package/modules/operator-auth/accounts/index.mjs +9 -0
  295. package/modules/operator-auth/accounts/native.d.mts +38 -0
  296. package/modules/operator-auth/accounts/native.mjs +171 -0
  297. package/modules/operator-auth/accounts/oa.mjs +90 -0
  298. package/modules/operator-auth/accounts/provider-control.mjs +50 -0
  299. package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
  300. package/modules/operator-auth/accounts/roles.mjs +26 -0
  301. package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
  302. package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
  303. package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
  304. package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
  305. package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
  306. package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
  307. package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
  308. package/modules/operator-auth/accounts/settings-page.mjs +4 -0
  309. package/modules/operator-auth/accounts/store.mjs +246 -0
  310. package/modules/operator-auth/branding.mjs +66 -0
  311. package/modules/operator-auth/config.mjs +35 -0
  312. package/modules/operator-auth/conformance/README.md +81 -0
  313. package/modules/operator-auth/conformance/index.d.mts +43 -0
  314. package/modules/operator-auth/conformance/index.mjs +300 -0
  315. package/modules/operator-auth/conformance/presentation.mjs +57 -0
  316. package/modules/operator-auth/crypto.mjs +17 -0
  317. package/modules/operator-auth/dev/badge.mjs +62 -0
  318. package/modules/operator-auth/dev/index.d.mts +14 -0
  319. package/modules/operator-auth/dev/index.mjs +137 -0
  320. package/modules/operator-auth/dev/personas.mjs +17 -0
  321. package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
  322. package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
  323. package/modules/operator-auth/dev-admission.d.mts +4 -0
  324. package/modules/operator-auth/dev-admission.mjs +10 -0
  325. package/modules/operator-auth/dev-gate.mjs +86 -0
  326. package/modules/operator-auth/docs/README.md +78 -0
  327. package/modules/operator-auth/docs/native-accounts.md +313 -0
  328. package/modules/operator-auth/docs/tanstack-start.md +218 -0
  329. package/modules/operator-auth/docs/vite-spa.md +144 -0
  330. package/modules/operator-auth/errors.mjs +9 -0
  331. package/modules/operator-auth/http.mjs +42 -0
  332. package/modules/operator-auth/identity/admission.mjs +105 -0
  333. package/modules/operator-auth/identity/after-confirm.mjs +18 -0
  334. package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
  335. package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
  336. package/modules/operator-auth/identity/email-link/index.mjs +128 -0
  337. package/modules/operator-auth/identity/email-link/message.mjs +16 -0
  338. package/modules/operator-auth/identity/index.d.mts +7 -0
  339. package/modules/operator-auth/identity/index.mjs +5 -0
  340. package/modules/operator-auth/identity/oidc/README.md +171 -0
  341. package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
  342. package/modules/operator-auth/identity/oidc/config.mjs +102 -0
  343. package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
  344. package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
  345. package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
  346. package/modules/operator-auth/identity/oidc/index.mjs +287 -0
  347. package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
  348. package/modules/operator-auth/identity/oidc/network.mjs +93 -0
  349. package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
  350. package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
  351. package/modules/operator-auth/identity/oidc/store.mjs +68 -0
  352. package/modules/operator-auth/identity/principal.mjs +28 -0
  353. package/modules/operator-auth/index.d.mts +223 -0
  354. package/modules/operator-auth/index.mjs +11 -0
  355. package/modules/operator-auth/input.mjs +55 -0
  356. package/modules/operator-auth/local-cli.mjs +16 -0
  357. package/modules/operator-auth/page.mjs +34 -0
  358. package/modules/operator-auth/policy.mjs +20 -0
  359. package/modules/operator-auth/routes.mjs +91 -0
  360. package/modules/operator-auth/service.mjs +152 -0
  361. package/modules/operator-auth/signin/index.mjs +67 -0
  362. package/modules/owner-claim/README.md +137 -0
  363. package/modules/owner-claim/idp.mjs +40 -0
  364. package/modules/owner-claim/index.d.mts +27 -0
  365. package/modules/owner-claim/index.mjs +39 -0
  366. package/modules/owner-claim/page.mjs +11 -0
  367. package/modules/owner-claim/schema/idp.sql +4 -0
  368. package/modules/owner-claim/schema/owner-claim.sql +18 -0
  369. package/modules/owner-claim/transaction.mjs +87 -0
  370. package/modules/owner-claim/verify.mjs +80 -0
  371. package/modules/portability/README.md +444 -0
  372. package/modules/portability/bridge.mjs +102 -0
  373. package/modules/portability/catalogue.mjs +118 -0
  374. package/modules/portability/cli-options.mjs +38 -0
  375. package/modules/portability/cli.mjs +56 -0
  376. package/modules/portability/copies.mjs +68 -0
  377. package/modules/portability/dev-data.mjs +32 -0
  378. package/modules/portability/diff.mjs +33 -0
  379. package/modules/portability/export.mjs +129 -0
  380. package/modules/portability/files.mjs +65 -0
  381. package/modules/portability/format.mjs +122 -0
  382. package/modules/portability/generated.mjs +60 -0
  383. package/modules/portability/hash.mjs +79 -0
  384. package/modules/portability/http.mjs +42 -0
  385. package/modules/portability/import.mjs +185 -0
  386. package/modules/portability/index.d.mts +58 -0
  387. package/modules/portability/index.mjs +22 -0
  388. package/modules/portability/journal.mjs +49 -0
  389. package/modules/portability/legacy-files.mjs +43 -0
  390. package/modules/portability/legacy-safety.mjs +27 -0
  391. package/modules/portability/lines.mjs +36 -0
  392. package/modules/portability/migration-history.mjs +18 -0
  393. package/modules/portability/migration-statements.mjs +23 -0
  394. package/modules/portability/payloads.mjs +52 -0
  395. package/modules/portability/provider-data.mjs +25 -0
  396. package/modules/portability/restore-safety.mjs +30 -0
  397. package/modules/portability/schema/portability.sql +25 -0
  398. package/modules/portability/storage.mjs +49 -0
  399. package/modules/release/README.md +212 -0
  400. package/modules/release/catalogue.mjs +79 -0
  401. package/modules/release/export.mjs +51 -0
  402. package/modules/release/produce.mjs +136 -0
  403. package/modules/release/public-child-transport-state.cjs +29 -0
  404. package/modules/release/public-child.mjs +71 -0
  405. package/modules/release/public-git.mjs +21 -0
  406. package/modules/release/publication-scan.mjs +22 -0
  407. package/modules/release/publish-handoff.mjs +37 -0
  408. package/modules/release/source.mjs +71 -0
  409. package/modules/release/test-summary.mjs +50 -0
  410. package/onboarding/README.md +163 -0
  411. package/onboarding/activity.mjs +31 -0
  412. package/onboarding/adapter-auth.mjs +88 -0
  413. package/onboarding/adapter-hosting.mjs +35 -0
  414. package/onboarding/adapter-labels.mjs +13 -0
  415. package/onboarding/adapter-settings.mjs +129 -0
  416. package/onboarding/adapter.mjs +238 -0
  417. package/onboarding/agent-handoff.mjs +9 -0
  418. package/onboarding/app-processes.mjs +21 -0
  419. package/onboarding/archive-output.d.mts +3 -0
  420. package/onboarding/archive-output.mjs +54 -0
  421. package/onboarding/command-custody.mjs +24 -0
  422. package/onboarding/command-recovery.mjs +8 -0
  423. package/onboarding/commands.mjs +166 -0
  424. package/onboarding/dependency-approval.mjs +36 -0
  425. package/onboarding/dev-identity.mjs +23 -0
  426. package/onboarding/email.mjs +24 -0
  427. package/onboarding/failures.mjs +42 -0
  428. package/onboarding/input-limits.mjs +7 -0
  429. package/onboarding/installer-inputs.mjs +24 -0
  430. package/onboarding/launcher.mjs +34 -0
  431. package/onboarding/local/build-files.mjs +64 -0
  432. package/onboarding/local/build.mjs +49 -0
  433. package/onboarding/local/environment.mjs +30 -0
  434. package/onboarding/local/files.mjs +35 -0
  435. package/onboarding/local/operation.mjs +166 -0
  436. package/onboarding/local/owner.mjs +34 -0
  437. package/onboarding/local/process-table.mjs +114 -0
  438. package/onboarding/local/recovery.mjs +79 -0
  439. package/onboarding/local/state.mjs +31 -0
  440. package/onboarding/metadata.mjs +7 -0
  441. package/onboarding/operator-install.mjs +45 -0
  442. package/onboarding/operator-installer.mjs +39 -0
  443. package/onboarding/operator-prerequisites.mjs +27 -0
  444. package/onboarding/package.json +6 -0
  445. package/onboarding/prefill.mjs +16 -0
  446. package/onboarding/private-files.mjs +97 -0
  447. package/onboarding/private.mjs +35 -0
  448. package/onboarding/process-cleanup.mjs +134 -0
  449. package/onboarding/process-diagnostics.mjs +91 -0
  450. package/onboarding/process-scope.mjs +118 -0
  451. package/onboarding/process-table.mjs +118 -0
  452. package/onboarding/process-tree.mjs +36 -0
  453. package/onboarding/production-modules.mjs +22 -0
  454. package/onboarding/production-options.mjs +40 -0
  455. package/onboarding/production-plan.mjs +8 -0
  456. package/onboarding/production-runtime.mjs +30 -0
  457. package/onboarding/project-vite.mjs +14 -0
  458. package/onboarding/providers.mjs +289 -0
  459. package/onboarding/queue.mjs +53 -0
  460. package/onboarding/queued-entry.mjs +34 -0
  461. package/onboarding/readiness.mjs +59 -0
  462. package/onboarding/redaction-jwt.mjs +32 -0
  463. package/onboarding/redaction.mjs +298 -0
  464. package/onboarding/schema/SETUP.schema.json +514 -0
  465. package/onboarding/schema/dependencies.mjs +35 -0
  466. package/onboarding/schema/json-schema.mjs +202 -0
  467. package/onboarding/schema/setup-validation.mjs +325 -0
  468. package/onboarding/secrets.mjs +21 -0
  469. package/onboarding/server.mjs +188 -0
  470. package/onboarding/setup-manifest.mjs +116 -0
  471. package/onboarding/state.mjs +46 -0
  472. package/onboarding/theme-files.mjs +37 -0
  473. package/onboarding/theme.mjs +118 -0
  474. package/onboarding/ui/activity.js +98 -0
  475. package/onboarding/ui/address-client.mjs +33 -0
  476. package/onboarding/ui/app.js +377 -0
  477. package/onboarding/ui/index.html +1 -0
  478. package/onboarding/ui/model-core.mjs +127 -0
  479. package/onboarding/ui/model.mjs +5 -0
  480. package/onboarding/ui/style.css +63 -0
  481. package/onboarding/ui/theme.css +37 -0
  482. package/onboarding/ui/workspace-art.js +30 -0
  483. package/onboarding/wrangler-auth.mjs +43 -0
  484. package/operations/README.md +246 -0
  485. package/operations/operations.mjs +168 -0
  486. package/operations/project-operations.mjs +157 -0
  487. package/package.json +11 -4
  488. package/scripts/publish-check/README.md +72 -0
  489. package/scripts/publish-check/checksum_maps.py +52 -0
  490. package/scripts/publish-check/cli.py +34 -0
  491. package/scripts/publish-check/command_arguments.py +51 -0
  492. package/scripts/publish-check/command_shell_quotes.py +41 -0
  493. package/scripts/publish-check/command_source_quotes.py +120 -0
  494. package/scripts/publish-check/content_identities.py +250 -0
  495. package/scripts/publish-check/copy_identities.py +41 -0
  496. package/scripts/publish-check/der.py +61 -0
  497. package/scripts/publish-check/destination.py +117 -0
  498. package/scripts/publish-check/env_values.py +74 -0
  499. package/scripts/publish-check/font_licence.py +23 -0
  500. package/scripts/publish-check/git_history.py +115 -0
  501. package/scripts/publish-check/github_refs.py +217 -0
  502. package/scripts/publish-check/gitleaks.py +72 -0
  503. package/scripts/publish-check/gitleaks.toml +40 -0
  504. package/scripts/publish-check/history_scan.py +176 -0
  505. package/scripts/publish-check/hit_location.py +18 -0
  506. package/scripts/publish-check/hygiene_config.py +42 -0
  507. package/scripts/publish-check/hygiene_text.py +11 -0
  508. package/scripts/publish-check/identity-recipe.mjs +54 -0
  509. package/scripts/publish-check/json_identity.py +79 -0
  510. package/scripts/publish-check/license-exceptions.json +11 -0
  511. package/scripts/publish-check/licenses.py +49 -0
  512. package/scripts/publish-check/policy.py +18 -0
  513. package/scripts/publish-check/project_identity.py +89 -0
  514. package/scripts/publish-check/provenance.py +91 -0
  515. package/scripts/publish-check/provenance_pins.py +33 -0
  516. package/scripts/publish-check/public_base.py +63 -0
  517. package/scripts/publish-check/public_hygiene.py +80 -0
  518. package/scripts/publish-check/publication-config.json +12 -0
  519. package/scripts/publish-check/publication_config.py +34 -0
  520. package/scripts/publish-check/report.py +52 -0
  521. package/scripts/publish-check/repository_commands.py +75 -0
  522. package/scripts/publish-check/repository_intent.py +52 -0
  523. package/scripts/publish-check/reviewed.py +41 -0
  524. package/scripts/publish-check/source_hashes.mjs +6 -0
  525. package/scripts/publish-check/tree_scan.py +88 -0
  526. package/scripts/publish-check/trust_bundle.py +87 -0
  527. package/scripts/publish-check.mjs +58 -0
@@ -0,0 +1,168 @@
1
+ import { readFile, access } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { relative, resolve } from 'node:path';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { spawnSync } from 'node:child_process';
6
+
7
+ function usefulEnvironmentSource(source) {
8
+ if (typeof source !== 'string') return false;
9
+ const value = source.trim();
10
+ if (source !== value || value.length < 8 || value.length > 180 || /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(value)) return false;
11
+ if (/(?:[a-z][a-z\d+.-]{1,20}:\/\/|www\.)/i.test(value)) return false;
12
+ if (/(?:[a-z\d-]+\.)+[a-z]{2,}(?::\d+)?(?:[/?#]\S*)?/i.test(value)
13
+ && !/\.(?:md|txt|jsonc?|ya?ml|toml|m?js|cjs|tsx?|jsx?|py|pdf|docx?|csv|env|sh|html?|xml|sql|log|rst|adoc|conf|ini)(?:[/:#][^\s]*)?$/i.test(value)) return false;
14
+ // Sources identify where to obtain a value, never assign one. Reject equals
15
+ // assignments regardless of the variable name and colon labels containing a
16
+ // credential term, including prefixed/suffixed names and quoted JSON keys.
17
+ if (value.includes('=')) return false;
18
+ if (/[\w.-]*(?:key|authorization|credential|password|passwd|secret|token|pwd)[\w.-]*["'`]?\s*:/i.test(value)) return false;
19
+ if (/\b(?:bearer|basic)\s+[A-Za-z0-9._~+/-]{12,}/i.test(value)) return false;
20
+ if (/\b(?:sk|pk|rk|ghp|gho|ghu|ghs|ghr|github_pat|xox[baprs]|glpat|AIza|AKIA)[_-][A-Za-z0-9._-]{8,}\b/i.test(value)) return false;
21
+ if (/\b(?:SG\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|whsec_[A-Za-z0-9]+|(?:AKIA|ASIA)[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{16,})\b/.test(value)) return false;
22
+ if (/(?<![A-Za-z0-9_-])[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/.test(value)) return false;
23
+ if (/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/.test(value)) return false;
24
+ return (value.match(/[A-Za-z0-9]+/g) ?? []).length >= 2;
25
+ }
26
+
27
+ export function validateEnvironmentSources(descriptor, knownNames) {
28
+ const sources = descriptor.environmentSources;
29
+ if (sources === undefined) return;
30
+ if (!sources || typeof sources !== 'object' || Array.isArray(sources)) throw new Error('Invalid environment source declaration.');
31
+ for (const [name, source] of Object.entries(sources)) {
32
+ if (!knownNames.has(name) || !usefulEnvironmentSource(source)) throw new Error('Invalid environment source declaration.');
33
+ }
34
+ }
35
+
36
+ export async function readOperations(root) {
37
+ const descriptor = JSON.parse(await readFile(resolve(root, 'OPERATIONS.json'), 'utf8'));
38
+ if (descriptor.version === 2) {
39
+ const { readProjectOperations } = await import('./project-operations.mjs');
40
+ return readProjectOperations(root, descriptor, validateEnvironmentSources);
41
+ }
42
+ if (descriptor.version !== 1 || typeof descriptor.edition !== 'string' || !descriptor.edition || !Array.isArray(descriptor.requiredEnvironment) || descriptor.requiredEnvironment.some(name => typeof name !== 'string' || !/^[A-Z][A-Z0-9_]*$/.test(name)) || descriptor.acceptance?.document !== 'ACCEPTANCE.md') throw new Error('Invalid operations descriptor.');
43
+ validateEnvironmentSources(descriptor, new Set(descriptor.requiredEnvironment));
44
+ await access(resolve(root, 'OPERATIONS.md'));
45
+ await access(resolve(root, descriptor.acceptance.document));
46
+ await access(resolve(root, 'scripts/operations-config.mjs'));
47
+ const pkg = JSON.parse(await readFile(resolve(root, 'package.json'), 'utf8'));
48
+ const commands = [];
49
+ for (const name of ['setup', 'deploy', 'verify']) {
50
+ const command = descriptor.commands?.[name];
51
+ const argv = command?.argv;
52
+ if (!Array.isArray(argv) || argv.length < 3 || argv.some(arg => typeof arg !== 'string' || !arg) || argv[0] !== 'pnpm' || argv[1] !== 'run' || typeof command.effects !== 'string' || !command.effects || typeof pkg.scripts?.[argv[2]] !== 'string') throw new Error('Invalid operation command.');
53
+ let available = true;
54
+ // Resolve script file references as well as the package-script entry.
55
+ if (available) for (const match of pkg.scripts[argv[2]].matchAll(/(?:^|\s)node\s+([\w./-]+\.mjs)(?=\s|$)/g)) {
56
+ try { await access(resolve(root, match[1])); } catch { available = false; }
57
+ }
58
+ commands.push({ name, available });
59
+ }
60
+ return { descriptor, commands };
61
+ }
62
+
63
+ async function inspectLoadedOperations(root, env, { descriptor, commands }) {
64
+ const missingEnvironment = descriptor.requiredEnvironment.filter(name => typeof env[name] !== 'string' || !env[name].trim());
65
+ let configuration = 'valid';
66
+ let loops = [];
67
+ if (descriptor.version === 2) {
68
+ const { inspectRequiredLoops } = await import('./project-operations.mjs');
69
+ loops = inspectRequiredLoops(descriptor, env);
70
+ }
71
+ try {
72
+ const module = descriptor.version === 2 ? descriptor.configuration?.module : 'scripts/operations-config.mjs';
73
+ if (module) {
74
+ const adapter = await import(pathToFileURL(resolve(root, module)));
75
+ await adapter.validateConfiguration(env);
76
+ }
77
+ } catch { configuration = 'invalid'; }
78
+ return { version: descriptor.version, ...(descriptor.version === 2 ? { project: descriptor.project, runtime: descriptor.runtime } : { edition: descriptor.edition }), scope: 'local-inputs-only', ok: commands.every(c => c.available) && !missingEnvironment.length && configuration === 'valid' && loops.every(loop => ['ready', 'optional-disabled'].includes(loop.status)), ...(descriptor.version === 2 ? { loops } : {}), commands, missingEnvironment, configuration,
79
+ unchecked: ['installed runtime versions and command executables', 'generated deployment caches and build artifacts', 'provider identity and permissions', 'remote resources and deployed health', 'provider credentials and delivery', 'product acceptance'],
80
+ guidance: 'See the project operating documentation. Configuration errors are suppressed to avoid disclosing input values.' };
81
+ }
82
+
83
+ export async function inspectOperations(root, env = process.env) {
84
+ return inspectLoadedOperations(root, env, await readOperations(root));
85
+ }
86
+
87
+ export async function describeOperations(root) {
88
+ const { descriptor, commands } = await readOperations(root);
89
+ if (commands.some(command => !command.available)) throw new Error('Unavailable operation target.');
90
+ return descriptor;
91
+ }
92
+
93
+ const displayArgv = argv => argv.map(argument => {
94
+ if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(argument)) return argument;
95
+ return `'${argument.replaceAll("'", "'\\''")}'`;
96
+ }).join(' ');
97
+ // A project without RER names this inspector by the path it was actually run from.
98
+ const invoked = root => displayArgv(['node', relative(root, process.argv[1] ?? 'operations.mjs')]);
99
+
100
+ async function checkSummary(result, descriptor, root) {
101
+ const missing = [...new Set([
102
+ ...result.missingEnvironment,
103
+ ...(result.loops ?? []).flatMap(loop => loop.missingEnvironment)
104
+ ])];
105
+ const documentation = descriptor.version === 1
106
+ ? 'OPERATIONS.md'
107
+ : descriptor.documentation?.document ?? 'the project setup documentation';
108
+ const declaredInputs = descriptor.version === 2
109
+ ? (await import('./project-operations.mjs')).knownEnvironmentNames(descriptor)
110
+ : new Set(descriptor.requiredEnvironment ?? []);
111
+ const lines = [];
112
+ if (missing.length) lines.push('Missing inputs are expected before a deployment is configured.');
113
+ else lines.push(result.ok
114
+ ? 'Operations check passed: declared local inputs expected before deployment are configured.'
115
+ : 'Operations check failed: resolve local configuration or target issues before deployment.');
116
+ if (missing.length) {
117
+ const sources = descriptor?.environmentSources ?? {};
118
+ lines.push('Missing inputs:');
119
+ for (const name of missing) lines.push(` ${name} — ${sources[name] ?? `see ${documentation}`}`);
120
+ lines.push('Configuration is checked again once these inputs are set.');
121
+ }
122
+ const unavailable = result.commands.filter(command => !command.available).map(command => command.name);
123
+ if (unavailable.length) lines.push(`Unavailable targets: ${unavailable.join(', ')}.`);
124
+ if (result.configuration === 'invalid' && !missing.length) {
125
+ lines.push('Configuration is invalid; validator details are suppressed.');
126
+ if (declaredInputs.size) lines.push(`Check the values you set for these declared input names: ${[...declaredInputs].sort().join(', ')}.`);
127
+ }
128
+ const blockedLoops = (result.loops ?? []).filter(loop => !['ready', 'optional-disabled'].includes(loop.status));
129
+ if (blockedLoops.length) lines.push(`Loops needing attention: ${blockedLoops.map(loop => `${loop.id} (${loop.status})`).join(', ')}.`);
130
+ if (result.ok) lines.push('Remote deployment, provider access, and product acceptance remain unchecked.');
131
+ else {
132
+ const setup = descriptor.commands?.setup;
133
+ if (setup?.argv) lines.push(`Next: read ${documentation}, then run the declared setup command when ready: ${displayArgv(setup.argv)}. For secondary descriptor inspection, run ${invoked(root)} describe.`);
134
+ else lines.push(`Next: read ${documentation} for setup guidance. No setup command is declared. For secondary descriptor inspection, run ${invoked(root)} describe.`);
135
+ }
136
+ return lines.join('\n');
137
+ }
138
+
139
+ export async function main(args = process.argv.slice(2), root = process.cwd()) {
140
+ const rer = resolve(root, 'scripts/rer.mjs');
141
+ if (!['describe', 'check'].includes(args[0]) || (args.length !== 1 && !existsSync(rer))) throw new Error('Unsupported operations command.');
142
+ // First-party projects describe and check through their RER command.
143
+ if (existsSync(rer)) {
144
+ process.exitCode = spawnSync(process.execPath, [rer, ...args], { cwd: root, stdio: 'inherit' }).status ?? 1;
145
+ return;
146
+ }
147
+ let result;
148
+ let descriptor;
149
+ if (args[0] === 'describe') result = await describeOperations(root);
150
+ else {
151
+ const loaded = await readOperations(root);
152
+ descriptor = loaded.descriptor;
153
+ result = await inspectLoadedOperations(root, process.env, loaded);
154
+ }
155
+ console.log(JSON.stringify(result, null, 2));
156
+ if (args[0] === 'check') {
157
+ console.error(await checkSummary(result, descriptor, root));
158
+ if (!result.ok) process.exitCode = 1;
159
+ }
160
+ }
161
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
162
+ try { await main(); } catch {
163
+ if (process.argv.slice(2).length === 1 && process.argv[2] === 'check') console.error('Operations check failed: descriptor or local configuration is invalid or unreadable; details are suppressed. Next: read OPERATIONS.md for recovery guidance, repair the descriptor or its referenced files, then rerun check.');
164
+ const usage = existsSync(resolve('scripts/rer.mjs')) ? 'node scripts/rer.mjs' : invoked(process.cwd());
165
+ console.error(JSON.stringify({ ok: false, error: `Invalid command or unreadable operations files. Usage: ${usage} describe|check` }));
166
+ process.exitCode = 1;
167
+ }
168
+ }
@@ -0,0 +1,157 @@
1
+ import { stat, realpath } from 'node:fs/promises';
2
+ import { isAbsolute, relative, resolve } from 'node:path';
3
+
4
+ const text = value => typeof value === 'string' && value.trim().length > 0;
5
+
6
+ const environmentName = value => typeof value === 'string' && value === value.trim() && /^[A-Z][A-Z0-9_]*$/.test(value);
7
+ const record = value => value !== null && typeof value === 'object' && !Array.isArray(value);
8
+ const has = (value, key) => Object.hasOwn(value, key);
9
+ const hostedProviders = ['sendgrid', 'resend', 'cloudflare'];
10
+ const fixedProviders = {
11
+ sendgrid: { apiKeyEnvironment: 'SENDGRID_API_KEY' },
12
+ resend: { apiKeyEnvironment: 'RESEND_API_KEY' },
13
+ cloudflare: { apiKeyEnvironment: 'CLOUDFLARE_EMAIL_API_TOKEN', accountIdEnvironment: 'CLOUDFLARE_EMAIL_ACCOUNT_ID' }
14
+ };
15
+
16
+ function fields(value, allowed) {
17
+ if (!record(value) || Object.keys(value).some(key => !allowed.includes(key))) throw new Error('Invalid loop declaration.');
18
+ }
19
+
20
+ function mailInputs(provider, declaration, fallback = false) {
21
+ fields(declaration, ['apiKeyEnvironment', 'requiredEnvironment', 'accountIdEnvironment', 'development', ...(fallback ? ['provider'] : [])]);
22
+ if (![...hostedProviders, ...(fallback ? [] : ['development'])].includes(provider)
23
+ || !Array.isArray(declaration.requiredEnvironment) || declaration.requiredEnvironment.some(name => !environmentName(name))) throw new Error('Invalid mail binding declaration.');
24
+ const names = [...declaration.requiredEnvironment];
25
+ if (provider === 'development') {
26
+ if (has(declaration, 'apiKeyEnvironment') || has(declaration, 'accountIdEnvironment')) throw new Error('Invalid development mail binding.');
27
+ fields(declaration.development, ['modeEnvironment', 'originEnvironment']);
28
+ if (!environmentName(declaration.development.modeEnvironment) || !environmentName(declaration.development.originEnvironment)) throw new Error('Invalid development mail binding.');
29
+ names.push(declaration.development.modeEnvironment, declaration.development.originEnvironment);
30
+ } else {
31
+ if (!environmentName(declaration.apiKeyEnvironment) || has(declaration, 'development')) throw new Error('Invalid hosted mail binding.');
32
+ names.unshift(declaration.apiKeyEnvironment);
33
+ if (provider === 'cloudflare') {
34
+ if (!environmentName(declaration.accountIdEnvironment)) throw new Error('Invalid hosted mail binding.');
35
+ names.push(declaration.accountIdEnvironment);
36
+ } else if (has(declaration, 'accountIdEnvironment')) throw new Error('Invalid hosted mail binding.');
37
+ }
38
+ return { ...declaration, provider, names: [...new Set(names)] };
39
+ }
40
+
41
+ // All consumers use the same validated input declarations, including disabled loops.
42
+ function normalizedLoops(descriptor) {
43
+ const loops = descriptor.loops === undefined ? [] : descriptor.loops;
44
+ if (!Array.isArray(loops) || new Set(loops.map(loop => loop?.id)).size !== loops.length) throw new Error('Invalid loop declarations.');
45
+ return loops.map(loop => {
46
+ fields(loop, ['id', 'type', 'required', 'enabled', ...(loop?.type === 'mail' ? ['provider', 'binding'] : [])]);
47
+ if (typeof loop.id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(loop.id) || typeof loop.required !== 'boolean' || typeof loop.enabled !== 'boolean' || !['worker', 'mail'].includes(loop.type)) throw new Error('Invalid loop declaration.');
48
+ if (loop.type === 'worker') return { ...loop, names: [] };
49
+ if (!has(loop, 'binding')) {
50
+ if (!hostedProviders.includes(loop.provider)) throw new Error('Invalid mail provider declaration.');
51
+ const selected = mailInputs(loop.provider, { ...fixedProviders[loop.provider], requiredEnvironment: [] });
52
+ return { ...loop, selected, names: ['MAIL_PROVIDER', ...selected.names] };
53
+ }
54
+ if (has(loop, 'provider')) throw new Error('Invalid mail binding declaration.');
55
+ const binding = loop.binding;
56
+ fields(binding, ['providerEnvironment', 'providers', 'whenUnset']);
57
+ if (!environmentName(binding.providerEnvironment) || !record(binding.providers) || !Object.keys(binding.providers).length) throw new Error('Invalid mail binding declaration.');
58
+ const providers = Object.fromEntries(Object.entries(binding.providers).map(([provider, inputs]) => [provider, mailInputs(provider, inputs)]));
59
+ const whenUnset = has(binding, 'whenUnset') ? mailInputs(binding.whenUnset?.provider, binding.whenUnset, true) : undefined;
60
+ return { ...loop, binding: { ...binding, providers, whenUnset }, names: [...new Set([binding.providerEnvironment, ...Object.values(providers).flatMap(inputs => inputs.names), ...(whenUnset?.names ?? [])])] };
61
+ });
62
+ }
63
+
64
+ export function knownEnvironmentNames(descriptor) {
65
+ return new Set([...descriptor.requiredEnvironment, ...normalizedLoops(descriptor).flatMap(loop => loop.names)]);
66
+ }
67
+
68
+ function localDevelopmentOrigin(value) {
69
+ if (typeof value !== 'string') return false;
70
+ try {
71
+ const url = new URL(value);
72
+ return ['http:', 'https:'].includes(url.protocol) && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)
73
+ && !url.username && !url.password && !url.search && !url.hash && url.pathname === '/'
74
+ && (value === url.origin || value === `${url.origin}/`);
75
+ } catch { return false; }
76
+ }
77
+
78
+ function inspectMailInputs(selected, env) {
79
+ const missingEnvironment = selected.names.filter(name => !text(env[name]));
80
+ let status = 'ready';
81
+ const missing = name => { if (!missingEnvironment.includes(name)) missingEnvironment.push(name); };
82
+ if (selected.accountIdEnvironment && (typeof env[selected.accountIdEnvironment] !== 'string' || !/^[a-f0-9]{32}$/i.test(env[selected.accountIdEnvironment]))) missing(selected.accountIdEnvironment);
83
+ if (selected.development) {
84
+ const { modeEnvironment, originEnvironment } = selected.development;
85
+ if (env[modeEnvironment] !== 'development') missing(modeEnvironment);
86
+ if (!localDevelopmentOrigin(env[originEnvironment])) missing(originEnvironment);
87
+ if (missingEnvironment.includes(modeEnvironment) || missingEnvironment.includes(originEnvironment)) status = 'invalid-development-mail';
88
+ }
89
+ if (missingEnvironment.length && status === 'ready') status = 'missing-inputs';
90
+ return { status, missingEnvironment };
91
+ }
92
+
93
+ // Descriptors can identify only files in their own checkout, including through symlinks.
94
+ async function localFile(root, path) {
95
+ if (!text(path) || isAbsolute(path) || path.split(/[\\/]/).includes('..')) throw new Error('Invalid project input path.');
96
+ const target = await realpath(resolve(root, path));
97
+ const inside = relative(await realpath(root), target);
98
+ if (inside.startsWith('..') || isAbsolute(inside)) throw new Error('Project input escapes checkout.');
99
+ if (!(await stat(target)).isFile()) throw new Error('Project input is not a file.');
100
+ }
101
+
102
+ export async function readProjectOperations(root, descriptor, validateSources) {
103
+ if (descriptor.seedContract !== undefined) {
104
+ const contract = descriptor.seedContract;
105
+ if (!contract || contract.version !== 1 || contract.profile !== 'first-party' || !text(contract.adapter)
106
+ || Object.keys(contract).some(key => !['version', 'profile', 'adapter'].includes(key))) throw new Error('Invalid seed command contract.');
107
+ await localFile(root, contract.adapter);
108
+ }
109
+ if (!text(descriptor.project) || !Array.isArray(descriptor.requiredEnvironment)
110
+ || descriptor.requiredEnvironment.some(name => !environmentName(name))
111
+ || !Array.isArray(descriptor.runtime?.requirements)
112
+ || descriptor.runtime.requirements.some(item => !text(item?.name) || !text(item?.version))
113
+ || !descriptor.commands || Array.isArray(descriptor.commands)
114
+ || !Object.keys(descriptor.commands).length) throw new Error('Invalid project operations descriptor.');
115
+ inspectRequiredLoops(descriptor, {});
116
+ if (typeof validateSources !== 'function') throw new Error('Environment source validation is unavailable.');
117
+ validateSources(descriptor, knownEnvironmentNames(descriptor));
118
+ for (const path of [descriptor.documentation?.document, descriptor.acceptance?.document, descriptor.configuration?.module].filter(value => value !== undefined)) await localFile(root, path);
119
+ const commands = [];
120
+ for (const [name, command] of Object.entries(descriptor.commands)) {
121
+ if (!/^[a-z][a-z0-9-]*$/.test(name) || !Array.isArray(command?.argv) || !command.argv.length
122
+ || command.argv.some(arg => typeof arg !== 'string') || !text(command.argv[0])
123
+ || !text(command.effects) || !Array.isArray(command.inputs)) throw new Error('Invalid project operation command.');
124
+ let available = true;
125
+ for (const path of command.inputs) {
126
+ // Malformed paths are descriptor errors; missing declared files are unavailable targets.
127
+ if (!text(path) || isAbsolute(path) || path.split(/[\\/]/).includes('..')) throw new Error('Invalid project input path.');
128
+ try { await localFile(root, path); } catch (error) {
129
+ if (error.code === 'ENOENT') available = false;
130
+ else throw error;
131
+ }
132
+ }
133
+ commands.push({ name, available });
134
+ }
135
+ return { descriptor, commands };
136
+ }
137
+
138
+ /** Declarative local loop checks. Never imports a module, sends mail, queries DNS or invokes a command. */
139
+ export function inspectRequiredLoops(descriptor, env) {
140
+ return normalizedLoops(descriptor).map(loop => {
141
+ let result = { status: 'ready', missingEnvironment: [] };
142
+ if (!loop.enabled) result.status = loop.required ? 'disabled-required' : 'optional-disabled';
143
+ else if (loop.type === 'mail') {
144
+ if (loop.binding) {
145
+ const { providerEnvironment, providers, whenUnset } = loop.binding;
146
+ const provider = env[providerEnvironment];
147
+ const selected = provider === undefined ? whenUnset : typeof provider === 'string' && has(providers, provider) ? providers[provider] : undefined;
148
+ result = selected ? inspectMailInputs(selected, env) : { status: 'invalid-mail-provider', missingEnvironment: [providerEnvironment] };
149
+ } else {
150
+ result = inspectMailInputs(loop.selected, env);
151
+ // Preserve the fixed-provider contract's truthy-selector mismatch behavior.
152
+ if (env.MAIL_PROVIDER && env.MAIL_PROVIDER !== loop.provider) result.status = 'mail-provider-mismatch';
153
+ }
154
+ }
155
+ return { id: loop.id, required: loop.required, ...result };
156
+ });
157
+ }
package/package.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "name": "@runeditrun/rer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "The rer command: install, set up, run and launch Run Edit Run checkouts.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "rer": "global/rer.mjs"
9
+ },
10
+ "engines": {
11
+ "node": ">=22"
12
+ }
13
+ }
@@ -0,0 +1,72 @@
1
+ # Publication scan implementation
2
+
3
+ See [setup, command and scope](../publish-check-README.md).
4
+
5
+ `git_history.py` delegates revision resolution, traversal, tree inventory and
6
+ blob reads to system Git (`rev-parse`, `rev-list`, `ls-tree`, `cat-file --batch`).
7
+ `gitleaks.py` runs the pinned scanner in git, directory and stdin modes and keeps
8
+ its report private until translating it into the value-free v3 envelope.
9
+ Hexadecimal credential assignments also use a Gitleaks rule without generic
10
+ entropy or English stopword filters. Both assignment rules go through the same
11
+ location-bound Source/module identity proof; provider and environment findings
12
+ remain independent.
13
+ `tree_scan.py` applies the kept text and licence policy. `history_scan.py`
14
+ assigns release, working-tree and historical scopes. The Node entry point bounds
15
+ the whole operation and stops its private process group on timeout/cancellation.
16
+
17
+ Binary metadata decoding, Git protocol/object/ref modelling, frozen comparison,
18
+ resumable inspection and their tests have been removed. Tests live in `tests.py`
19
+ and `coverage_tests.py` and construct disposable repositories with actual Git.
20
+
21
+ `content_identities.py` classifies only exact generic-api-key occurrences at
22
+ proved Source key locations. It requires same-tree edition/lock selections,
23
+ manifest and archive hashes, valid retained file records, and exact native match
24
+ bytes. Retained archive bytes must hash to the maintained Source serializer's
25
+ `hash(json(saved))` as well as the selected pin. `source_hashes.mjs` reuses that
26
+ serializer. Duplicate JSON fields, non-JSON constants (including ignored fields),
27
+ incorrect types, missing preimages, mismatched pins and unrecognized locations keep
28
+ the native finding. Each selection's complete dependency closure must have valid
29
+ archive and pin proofs before any location in that graph is allowed; unrelated
30
+ coherent selections remain eligible. Normal origin repositories obey the maintained
31
+ Source schema, including rejection of strings starting with `-`.
32
+ Original/private keys and arbitrary copies of a known digest
33
+ are not proofs. Provider and environment findings are unaffected. Native git/dir
34
+ matches are held only in the private temporary report and memory for this proof;
35
+ matched values are never added to the public report. This classification does not
36
+ replace or relax the edition-authoring check.
37
+
38
+ `copy_identities.py` proves the maintained `base-copies.json` and portable
39
+ `base/PROVENANCE.json` copy-inventory SHA-256 values
40
+ against every declared file's bytes and regular-file mode in the same scanned
41
+ tree. Historical matches use only that commit's blobs; present files cannot
42
+ rescue a historical mismatch. Working-tree preimages reject linked paths.
43
+ Only the exact native match at a proved inventory field is classified. There
44
+ is no filename, hash-value or comma-delimited credential exemption.
45
+
46
+ `checksum_maps.py` classifies a file-name to digest map (`'0005_operator_auth.sql':
47
+ '<sha256>'`, in JSON, JavaScript, YAML or similar) in any file outside the
48
+ content-identity documents above. The native match must sit in a run of
49
+ `name: digest` entries separated only by commas, semicolons, whitespace and line
50
+ comments, and every entry in that run must name a regular file in the same scanned
51
+ tree (a path, or a trailing path component) whose bytes hash to that SHA-256. One
52
+ entry that does not prove, a 40-hex value, a key that names no tree file, or a hex
53
+ value with no file-name key keeps the native finding. Historical matches use only
54
+ that commit's blobs and working-tree preimages reject linked paths. There is no
55
+ path, filename, hash-value or repository exemption. `hit_location.py` resolves
56
+ the native match's exact bytes for both proofs.
57
+
58
+ `trust_bundle.py` classifies an email match as public CA certificate metadata
59
+ only when all of these hold: the file's path is an artifact listed by a committed
60
+ `trust-bundle.json` in its directory and its bytes hash to the SHA-256 pinned
61
+ there; that manifest's webpki-roots release and checksum are exactly the one the
62
+ same directory's `Cargo.lock` locks from crates.io; and the match lies entirely
63
+ inside an attribute value of a subject Name parsed (`der.py`, strict DER) from
64
+ those bytes, where the anchor's subject Name and SubjectPublicKeyInfo DER both
65
+ occur in the file and hash to one anchor entry of the manifest. With the verified
66
+ subject values blanked, no email may remain, and every match must be one found
67
+ inside them; otherwise the file keeps `hygiene.email`. Historical blobs use their
68
+ own commit's manifest and lock; working-tree files use the selected commit's.
69
+ The build that produces the artifacts (`modules/egress-service/tls/build.mjs`)
70
+ regenerates the manifest, and its `--check` mode rejects a stale one. An
71
+ alphabetic top-level domain label ends before a following digit, so a match
72
+ ends at the DER string's boundary rather than absorbing the next tag byte.
@@ -0,0 +1,52 @@
1
+ """Classify file-name to SHA-256 maps whose every digest is a same-tree file's hash.
2
+
3
+ A map is a run of `name: "digest"` entries separated only by commas, semicolons,
4
+ whitespace and line comments. The native finding inside it is classified only
5
+ when every entry names a regular file in the scanned tree whose bytes hash to
6
+ that entry's digest. One unproved entry, or a hex value no entry keys to a file,
7
+ keeps the finding. No path, filename or repository is exempt.
8
+ """
9
+ import hashlib
10
+ import re
11
+ from copy_identities import relative_path
12
+ from hit_location import match_spans
13
+
14
+ ENTRY = re.compile(
15
+ rb'''(?<![\w./@+-])(?:(?P<q>["'`])(?P<quoted>[^"'`\\\r\n]{1,200})(?P=q)|(?P<bare>[\w./@+-]{1,200}))'''
16
+ rb'''[ \t]*(?::|=>|=)[ \t]*(?P<v>["'`]?)(?P<digest>[a-f0-9]{64}|[a-f0-9]{40})(?P=v)(?![a-f0-9\w])''')
17
+ SEPARATOR = re.compile(rb'(?:[\s,;]|(?://|#)[^\n]*)*')
18
+
19
+
20
+ class ChecksumMaps:
21
+ def __init__(self, paths, read_regular):
22
+ self.paths, self.read_regular, self.hashes = sorted(paths), read_regular, {}
23
+
24
+ def digest(self, path):
25
+ if path not in self.hashes:
26
+ value = self.read_regular(path)
27
+ self.hashes[path] = hashlib.sha256(value[0]).hexdigest() if value else None
28
+ return self.hashes[path]
29
+
30
+ def names_file(self, name, digest):
31
+ return (relative_path(name) and len(digest) == 64
32
+ and any(self.digest(path) == digest for path in self.paths
33
+ if path == name or path.endswith('/' + name)))
34
+
35
+ def matches(self, data, hit):
36
+ if hit.get('RuleID') not in ('generic-api-key', 'hex-api-key'):
37
+ return False
38
+ entries = list(ENTRY.finditer(data))
39
+ for left, right in match_spans(data, hit):
40
+ keyed = [i for i, e in enumerate(entries)
41
+ if left <= e.start('digest') and e.end('digest') <= right
42
+ and e.group('digest').decode() == hit.get('Secret')]
43
+ if len(keyed) != 1:
44
+ continue
45
+ first = last = keyed[0]
46
+ while first > 0 and SEPARATOR.fullmatch(data, entries[first - 1].end(), entries[first].start()):
47
+ first -= 1
48
+ while last + 1 < len(entries) and SEPARATOR.fullmatch(data, entries[last].end(), entries[last + 1].start()):
49
+ last += 1
50
+ return all(self.names_file((e.group('quoted') or e.group('bare')).decode('utf-8', 'replace'),
51
+ e.group('digest').decode()) for e in entries[first:last + 1])
52
+ return False
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import json
4
+ from history_scan import scan
5
+
6
+
7
+ class SafeParser(argparse.ArgumentParser):
8
+ def error(self, message):
9
+ self.exit(2, 'publish-check invalid arguments\n')
10
+
11
+
12
+ def seconds(value):
13
+ result = float(value)
14
+ if not 0 < result <= 7200:
15
+ raise argparse.ArgumentTypeError('invalid timeout')
16
+ return result
17
+
18
+
19
+ def main():
20
+ parser = SafeParser(description='Scan working files and full history with system Git and pinned Gitleaks.')
21
+ parser.add_argument('--repo', required=True)
22
+ parser.add_argument('--ref', default='HEAD')
23
+ for option in ('public-base', 'env-file', 'project', 'repository', 'cache-dir'):
24
+ parser.add_argument('--' + option)
25
+ parser.add_argument('--timeout-seconds', type=seconds, default=300)
26
+ args = parser.parse_args()
27
+ report = scan(args.repo, args.ref, args.public_base, args.env_file, args.project, args.repository,
28
+ cache_dir=args.cache_dir, timeout=args.timeout_seconds)
29
+ print(json.dumps(report))
30
+ return {'PASS': 0, 'FAIL': 1, 'INCOMPLETE': 2}[report['status']]
31
+
32
+
33
+ if __name__ == '__main__':
34
+ raise SystemExit(main())
@@ -0,0 +1,51 @@
1
+ """Retain completed shell words and quoted parts before malformed outer text."""
2
+ import shlex
3
+
4
+
5
+ def completed_parts(source):
6
+ """Read completed components of the final, unfinished shell word.
7
+
8
+ An unknown wrapper can leave its closing quote after a complete quoted
9
+ command argument. shlex keeps that argument in its pending token and then
10
+ raises. Keep its closed quote components and leading unquoted component;
11
+ unquoted suffixes between later source quotes are not command arguments.
12
+ """
13
+ parts, leading, quoted = [], '', False
14
+ delimiter, opened, index = None, 0, 0
15
+ while index < len(source):
16
+ char = source[index]
17
+ if char == '\\' and delimiter != "'":
18
+ if not delimiter and not quoted:
19
+ leading += source[index:index + 2]
20
+ index += 2
21
+ continue
22
+ if delimiter:
23
+ if char == delimiter:
24
+ parts.extend(shlex.split(source[opened:index + 1], posix=True))
25
+ delimiter = None
26
+ elif char in ' \t\r\n':
27
+ parts, leading, quoted = [], '', False
28
+ elif char in '#;&|':
29
+ return []
30
+ elif char in "'\"":
31
+ if not quoted and leading:
32
+ parts.extend(shlex.split(leading, posix=True))
33
+ delimiter, opened, quoted = char, index, True
34
+ elif not quoted:
35
+ leading += char
36
+ index += 1
37
+ return parts
38
+
39
+
40
+ def arguments(source):
41
+ shell = shlex.shlex(source, posix=True, punctuation_chars=';&|')
42
+ shell.whitespace_split = True
43
+ try:
44
+ for word in shell:
45
+ if word and all(char in ';&|' for char in word):
46
+ return
47
+ yield word
48
+ except ValueError:
49
+ # Completed words have already been yielded. A completed quoted part
50
+ # of the pending word must not disappear with a later unmatched quote.
51
+ yield from completed_parts(source)
@@ -0,0 +1,41 @@
1
+ """Bound literal arguments in documentary shell output commands."""
2
+ import re
3
+ import shlex
4
+
5
+ OUTPUT = re.compile(r'(?m)^[ \t]*(?:(?:command|builtin)[ \t]+)?(?:echo|printf)[ \t]+')
6
+
7
+
8
+ def output_quotes(source):
9
+ """Recognize explicit echo/printf statements without running a shell.
10
+
11
+ Arbitrary prose is often valid shell syntax, so shell tokenization alone
12
+ cannot establish a source context. These output statements explicitly
13
+ present their arguments as text. Custom executable names make no such
14
+ assertion about a document's prose.
15
+ """
16
+ found = set()
17
+ for match in OUTPUT.finditer(source):
18
+ quotes, delimiter = set(), None
19
+ index = match.end()
20
+ while index < len(source):
21
+ char = source[index]
22
+ if char == '\\' and delimiter != "'":
23
+ index += 2
24
+ continue
25
+ if delimiter:
26
+ if char == delimiter:
27
+ delimiter = None
28
+ elif char in '\n;&|':
29
+ break
30
+ elif char == '#' and source[index - 1].isspace():
31
+ break
32
+ elif char in "'\"":
33
+ delimiter = char
34
+ quotes.add(index)
35
+ index += 1
36
+ try:
37
+ shlex.split(source[match.start():index], comments=True, posix=True)
38
+ except ValueError:
39
+ continue
40
+ found.update(quotes)
41
+ return found