@runeditrun/rer 0.0.0-stage → 0.1.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 (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -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 +177 -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 +155 -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 +129 -0
  71. package/global/launch/facts.mjs +93 -0
  72. package/global/launch/failure.mjs +5 -0
  73. package/global/launch/local-defaults.mjs +4 -0
  74. package/global/launch/prepare.mjs +61 -0
  75. package/global/launch/render.mjs +44 -0
  76. package/global/launch/state.mjs +97 -0
  77. package/global/launch/steps.mjs +81 -0
  78. package/global/launch/worker.mjs +43 -0
  79. package/global/list.mjs +59 -0
  80. package/global/main.mjs +53 -0
  81. package/global/package-info.mjs +38 -0
  82. package/global/registry.mjs +24 -0
  83. package/global/rer.mjs +29 -0
  84. package/global/update/archive.mjs +89 -0
  85. package/global/update/command.mjs +90 -0
  86. package/global/update/latest.mjs +6 -0
  87. package/global/update/notice.mjs +58 -0
  88. package/global/update/registry.mjs +47 -0
  89. package/global/version.mjs +29 -0
  90. package/install.sh +144 -0
  91. package/modules/ai/LICENSE +21 -0
  92. package/modules/ai/README.md +90 -0
  93. package/modules/ai/client.mjs +71 -0
  94. package/modules/ai/config.mjs +39 -0
  95. package/modules/ai/dev/index.d.mts +6 -0
  96. package/modules/ai/dev/index.mjs +64 -0
  97. package/modules/ai/dev/outbox.mjs +33 -0
  98. package/modules/ai/errors.mjs +28 -0
  99. package/modules/ai/fake/index.d.mts +3 -0
  100. package/modules/ai/fake/index.mjs +82 -0
  101. package/modules/ai/index.d.mts +10 -0
  102. package/modules/ai/index.mjs +3 -0
  103. package/modules/ai/package.json +6 -0
  104. package/modules/container/README.md +156 -0
  105. package/modules/container/adapters/d1.mjs +34 -0
  106. package/modules/container/adapters/egress.mjs +40 -0
  107. package/modules/container/adapters/jobs.mjs +47 -0
  108. package/modules/container/adapters/objects.mjs +84 -0
  109. package/modules/container/adapters/retained.mjs +14 -0
  110. package/modules/container/adapters/sqlite-error.mjs +7 -0
  111. package/modules/container/adapters/sqlite-worker.mjs +26 -0
  112. package/modules/container/assets.mjs +14 -0
  113. package/modules/container/bindings.mjs +1 -0
  114. package/modules/container/boundary.mjs +38 -0
  115. package/modules/container/build.mjs +65 -0
  116. package/modules/container/deploy-input.mjs +17 -0
  117. package/modules/container/entrypoint.sh +5 -0
  118. package/modules/container/host.mjs +43 -0
  119. package/modules/container/ingress.mjs +23 -0
  120. package/modules/container/lock.mjs +11 -0
  121. package/modules/container/migrations.mjs +16 -0
  122. package/modules/container/node-engine.mjs +39 -0
  123. package/modules/container/portability-schema.mjs +14 -0
  124. package/modules/container/remote/deploy.py +76 -0
  125. package/modules/container/remote/files.py +275 -0
  126. package/modules/container/remote/resources.py +157 -0
  127. package/modules/container/runner.mjs +10 -0
  128. package/modules/container/ssh-plan.mjs +44 -0
  129. package/modules/container/ssh.mjs +63 -0
  130. package/modules/container/transfer.mjs +27 -0
  131. package/modules/container/worker-engine.mjs +32 -0
  132. package/modules/dependencies/README.md +211 -0
  133. package/modules/dependencies/approval.mjs +51 -0
  134. package/modules/dependencies/auth.mjs +40 -0
  135. package/modules/dependencies/authority.mjs +14 -0
  136. package/modules/dependencies/crypto.mjs +13 -0
  137. package/modules/dependencies/index.d.mts +23 -0
  138. package/modules/dependencies/index.mjs +113 -0
  139. package/modules/dependencies/logout.mjs +42 -0
  140. package/modules/dependencies/native.mjs +9 -0
  141. package/modules/dependencies/protocol.mjs +23 -0
  142. package/modules/dependencies/schema/approval-attempt.sql +3 -0
  143. package/modules/dependencies/schema/dependencies.sql +15 -0
  144. package/modules/dependencies/store.mjs +19 -0
  145. package/modules/deploy-kit/README.md +308 -0
  146. package/modules/deploy-kit/artifact.mjs +47 -0
  147. package/modules/deploy-kit/build-modules.mjs +21 -0
  148. package/modules/deploy-kit/check.mjs +13 -0
  149. package/modules/deploy-kit/cli.mjs +50 -0
  150. package/modules/deploy-kit/container-config.mjs +8 -0
  151. package/modules/deploy-kit/default-stage.mjs +14 -0
  152. package/modules/deploy-kit/deploy.mjs +80 -0
  153. package/modules/deploy-kit/health-request.mjs +86 -0
  154. package/modules/deploy-kit/health.mjs +99 -0
  155. package/modules/deploy-kit/index.mjs +12 -0
  156. package/modules/deploy-kit/local-only.mjs +10 -0
  157. package/modules/deploy-kit/migrations.mjs +48 -0
  158. package/modules/deploy-kit/owner-cli.mjs +63 -0
  159. package/modules/deploy-kit/owner.mjs +297 -0
  160. package/modules/deploy-kit/prepare.mjs +62 -0
  161. package/modules/deploy-kit/provider.mjs +99 -0
  162. package/modules/deploy-kit/queue-consumer.mjs +10 -0
  163. package/modules/deploy-kit/receipt.mjs +16 -0
  164. package/modules/deploy-kit/render.mjs +47 -0
  165. package/modules/deploy-kit/resources.mjs +106 -0
  166. package/modules/deploy-kit/service-grants.mjs +25 -0
  167. package/modules/deploy-kit/sql-safety.mjs +94 -0
  168. package/modules/deploy-kit/sql-tokens.mjs +49 -0
  169. package/modules/deploy-kit/teardown.mjs +100 -0
  170. package/modules/deploy-kit/templates.mjs +31 -0
  171. package/modules/deploy-kit/validation.mjs +58 -0
  172. package/modules/deploy-kit/worker.mjs +105 -0
  173. package/modules/erasure/CONTRACT.md +90 -0
  174. package/modules/erasure/README.md +221 -0
  175. package/modules/erasure/audit.mjs +23 -0
  176. package/modules/erasure/blobs.mjs +45 -0
  177. package/modules/erasure/cascade.mjs +91 -0
  178. package/modules/erasure/completion.mjs +62 -0
  179. package/modules/erasure/declaration.mjs +51 -0
  180. package/modules/erasure/fence.mjs +19 -0
  181. package/modules/erasure/index.d.mts +62 -0
  182. package/modules/erasure/index.mjs +85 -0
  183. package/modules/erasure/order.mjs +56 -0
  184. package/modules/erasure/plan.mjs +121 -0
  185. package/modules/erasure/rate.mjs +57 -0
  186. package/modules/erasure/receipt.mjs +32 -0
  187. package/modules/erasure/root.mjs +21 -0
  188. package/modules/erasure/schema/erasure-rates.sql +5 -0
  189. package/modules/erasure/schema/erasure.sql +27 -0
  190. package/modules/erasure/scope.mjs +29 -0
  191. package/modules/erasure/sql.mjs +23 -0
  192. package/modules/erasure/verification.mjs +69 -0
  193. package/modules/integrations/core/CONTRACT.md +50 -0
  194. package/modules/integrations/core/README.md +27 -0
  195. package/modules/integrations/core/crypto.ts +19 -0
  196. package/modules/integrations/core/database.ts +21 -0
  197. package/modules/integrations/core/delivery.ts +97 -0
  198. package/modules/integrations/core/destination.ts +13 -0
  199. package/modules/integrations/core/index.ts +7 -0
  200. package/modules/integrations/core/node-transport.ts +72 -0
  201. package/modules/integrations/core/public-address.ts +57 -0
  202. package/modules/integrations/core/repository.ts +74 -0
  203. package/modules/integrations/core/schema.sql +36 -0
  204. package/modules/integrations/core/sqlite.ts +20 -0
  205. package/modules/integrations/core/timestamped-body-signature.ts +38 -0
  206. package/modules/integrations/core/tokens.ts +71 -0
  207. package/modules/integrations/core/types.ts +52 -0
  208. package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
  209. package/modules/integrations/core/webhook.ts +73 -0
  210. package/modules/job-queue/README.md +173 -0
  211. package/modules/job-queue/cli.mjs +64 -0
  212. package/modules/job-queue/command.mjs +64 -0
  213. package/modules/job-queue/config.mjs +48 -0
  214. package/modules/job-queue/identity.mjs +48 -0
  215. package/modules/job-queue/index.mjs +135 -0
  216. package/modules/job-queue/membership.mjs +30 -0
  217. package/modules/job-queue/module.json +10 -0
  218. package/modules/job-queue/policy.mjs +29 -0
  219. package/modules/job-queue/processes.mjs +80 -0
  220. package/modules/job-queue/resources.mjs +37 -0
  221. package/modules/job-queue/storage.mjs +64 -0
  222. package/modules/job-queue/supervisor.mjs +30 -0
  223. package/modules/job-queue/waiting.mjs +16 -0
  224. package/modules/mail/LICENSE +21 -0
  225. package/modules/mail/README.md +336 -0
  226. package/modules/mail/doctor.mjs +81 -0
  227. package/modules/mail/index.mjs +8 -0
  228. package/modules/mail/live-proof/README.md +113 -0
  229. package/modules/mail/live-proof/run.mjs +55 -0
  230. package/modules/mail/package.json +19 -0
  231. package/modules/mail/pnpm-lock.yaml +537 -0
  232. package/modules/mail/src/adapters/cloudflare/README.md +84 -0
  233. package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
  234. package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
  235. package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
  236. package/modules/mail/src/adapters/messaging/README.md +92 -0
  237. package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
  238. package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
  239. package/modules/mail/src/adapters/messaging/index.mjs +61 -0
  240. package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
  241. package/modules/mail/src/adapters/messaging/request.mjs +52 -0
  242. package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
  243. package/modules/mail/src/adapters/resend/README.md +120 -0
  244. package/modules/mail/src/adapters/resend/index.mjs +22 -0
  245. package/modules/mail/src/adapters/resend/response.mjs +54 -0
  246. package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
  247. package/modules/mail/src/adapters/resend/size.mjs +99 -0
  248. package/modules/mail/src/adapters/sendgrid/README.md +75 -0
  249. package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
  250. package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
  251. package/modules/mail/src/core/errors.mjs +9 -0
  252. package/modules/mail/src/core/transport.mjs +33 -0
  253. package/modules/mail/src/core/validation.mjs +172 -0
  254. package/modules/mail/src/development/index.d.mts +37 -0
  255. package/modules/mail/src/development/index.mjs +35 -0
  256. package/modules/mail/src/development/outbox.mjs +78 -0
  257. package/modules/mail/src/development/schema.sql +9 -0
  258. package/modules/mail/src/development/viewer-html.mjs +42 -0
  259. package/modules/mail/src/development/viewer.mjs +65 -0
  260. package/modules/mail/src/doctor/README.md +107 -0
  261. package/modules/mail/src/doctor/common.mjs +64 -0
  262. package/modules/mail/src/doctor/dns.mjs +102 -0
  263. package/modules/mail/src/doctor/index.mjs +28 -0
  264. package/modules/mail/src/doctor/probe.mjs +66 -0
  265. package/modules/mail/src/doctor/receive.mjs +81 -0
  266. package/modules/mail/src/doctor/records.mjs +45 -0
  267. package/modules/mail/src/events/README.md +268 -0
  268. package/modules/mail/src/events/d1-schema.sql +25 -0
  269. package/modules/mail/src/events/d1-storage.mjs +76 -0
  270. package/modules/mail/src/events/index.mjs +3 -0
  271. package/modules/mail/src/events/intake.mjs +201 -0
  272. package/modules/mail/src/events/storage.mjs +52 -0
  273. package/modules/mail/src/events/verify.mjs +92 -0
  274. package/modules/mail/src/inbound/README.md +137 -0
  275. package/modules/mail/src/inbound/bytes.mjs +91 -0
  276. package/modules/mail/src/inbound/headers.mjs +143 -0
  277. package/modules/mail/src/inbound/index.mjs +29 -0
  278. package/modules/mail/src/inbound/mime.mjs +36 -0
  279. package/modules/mail/src/inbound/multipart.mjs +27 -0
  280. package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
  281. package/modules/mail/src/inbound/resend.mjs +54 -0
  282. package/modules/mail/src/inbound/sendgrid.mjs +60 -0
  283. package/modules/mail/src/sender.mjs +33 -0
  284. package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
  285. package/modules/mail/src/sendgrid-email/index.mjs +86 -0
  286. package/modules/mail/src/unsubscribe/index.mjs +142 -0
  287. package/modules/operator-auth/README.md +599 -0
  288. package/modules/operator-auth/accounts/adapter.mjs +41 -0
  289. package/modules/operator-auth/accounts/admin.mjs +159 -0
  290. package/modules/operator-auth/accounts/credentials.mjs +27 -0
  291. package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
  292. package/modules/operator-auth/accounts/index.d.mts +9 -0
  293. package/modules/operator-auth/accounts/index.mjs +9 -0
  294. package/modules/operator-auth/accounts/native.d.mts +38 -0
  295. package/modules/operator-auth/accounts/native.mjs +171 -0
  296. package/modules/operator-auth/accounts/oa.mjs +90 -0
  297. package/modules/operator-auth/accounts/provider-control.mjs +50 -0
  298. package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
  299. package/modules/operator-auth/accounts/roles.mjs +26 -0
  300. package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
  301. package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
  302. package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
  303. package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
  304. package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
  305. package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
  306. package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
  307. package/modules/operator-auth/accounts/settings-page.mjs +4 -0
  308. package/modules/operator-auth/accounts/store.mjs +246 -0
  309. package/modules/operator-auth/branding.mjs +66 -0
  310. package/modules/operator-auth/config.mjs +35 -0
  311. package/modules/operator-auth/conformance/README.md +81 -0
  312. package/modules/operator-auth/conformance/index.d.mts +43 -0
  313. package/modules/operator-auth/conformance/index.mjs +300 -0
  314. package/modules/operator-auth/conformance/presentation.mjs +57 -0
  315. package/modules/operator-auth/crypto.mjs +17 -0
  316. package/modules/operator-auth/dev/badge.mjs +62 -0
  317. package/modules/operator-auth/dev/index.d.mts +14 -0
  318. package/modules/operator-auth/dev/index.mjs +137 -0
  319. package/modules/operator-auth/dev/personas.mjs +17 -0
  320. package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
  321. package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
  322. package/modules/operator-auth/dev-admission.d.mts +4 -0
  323. package/modules/operator-auth/dev-admission.mjs +10 -0
  324. package/modules/operator-auth/dev-gate.mjs +86 -0
  325. package/modules/operator-auth/docs/README.md +78 -0
  326. package/modules/operator-auth/docs/native-accounts.md +313 -0
  327. package/modules/operator-auth/docs/tanstack-start.md +218 -0
  328. package/modules/operator-auth/docs/vite-spa.md +144 -0
  329. package/modules/operator-auth/errors.mjs +9 -0
  330. package/modules/operator-auth/http.mjs +42 -0
  331. package/modules/operator-auth/identity/admission.mjs +105 -0
  332. package/modules/operator-auth/identity/after-confirm.mjs +18 -0
  333. package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
  334. package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
  335. package/modules/operator-auth/identity/email-link/index.mjs +128 -0
  336. package/modules/operator-auth/identity/email-link/message.mjs +16 -0
  337. package/modules/operator-auth/identity/index.d.mts +7 -0
  338. package/modules/operator-auth/identity/index.mjs +5 -0
  339. package/modules/operator-auth/identity/oidc/README.md +171 -0
  340. package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
  341. package/modules/operator-auth/identity/oidc/config.mjs +102 -0
  342. package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
  343. package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
  344. package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
  345. package/modules/operator-auth/identity/oidc/index.mjs +287 -0
  346. package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
  347. package/modules/operator-auth/identity/oidc/network.mjs +93 -0
  348. package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
  349. package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
  350. package/modules/operator-auth/identity/oidc/store.mjs +68 -0
  351. package/modules/operator-auth/identity/principal.mjs +28 -0
  352. package/modules/operator-auth/index.d.mts +223 -0
  353. package/modules/operator-auth/index.mjs +11 -0
  354. package/modules/operator-auth/input.mjs +55 -0
  355. package/modules/operator-auth/local-cli.mjs +16 -0
  356. package/modules/operator-auth/page.mjs +34 -0
  357. package/modules/operator-auth/policy.mjs +20 -0
  358. package/modules/operator-auth/routes.mjs +91 -0
  359. package/modules/operator-auth/service.mjs +152 -0
  360. package/modules/operator-auth/signin/index.mjs +67 -0
  361. package/modules/owner-claim/README.md +137 -0
  362. package/modules/owner-claim/idp.mjs +40 -0
  363. package/modules/owner-claim/index.d.mts +27 -0
  364. package/modules/owner-claim/index.mjs +39 -0
  365. package/modules/owner-claim/page.mjs +11 -0
  366. package/modules/owner-claim/schema/idp.sql +4 -0
  367. package/modules/owner-claim/schema/owner-claim.sql +18 -0
  368. package/modules/owner-claim/transaction.mjs +87 -0
  369. package/modules/owner-claim/verify.mjs +80 -0
  370. package/modules/portability/README.md +444 -0
  371. package/modules/portability/bridge.mjs +102 -0
  372. package/modules/portability/catalogue.mjs +118 -0
  373. package/modules/portability/cli-options.mjs +38 -0
  374. package/modules/portability/cli.mjs +56 -0
  375. package/modules/portability/copies.mjs +68 -0
  376. package/modules/portability/dev-data.mjs +32 -0
  377. package/modules/portability/diff.mjs +33 -0
  378. package/modules/portability/export.mjs +129 -0
  379. package/modules/portability/files.mjs +65 -0
  380. package/modules/portability/format.mjs +122 -0
  381. package/modules/portability/generated.mjs +60 -0
  382. package/modules/portability/hash.mjs +79 -0
  383. package/modules/portability/http.mjs +42 -0
  384. package/modules/portability/import.mjs +185 -0
  385. package/modules/portability/index.d.mts +58 -0
  386. package/modules/portability/index.mjs +22 -0
  387. package/modules/portability/journal.mjs +49 -0
  388. package/modules/portability/legacy-files.mjs +43 -0
  389. package/modules/portability/legacy-safety.mjs +27 -0
  390. package/modules/portability/lines.mjs +36 -0
  391. package/modules/portability/migration-history.mjs +18 -0
  392. package/modules/portability/migration-statements.mjs +23 -0
  393. package/modules/portability/payloads.mjs +52 -0
  394. package/modules/portability/provider-data.mjs +25 -0
  395. package/modules/portability/restore-safety.mjs +30 -0
  396. package/modules/portability/schema/portability.sql +25 -0
  397. package/modules/portability/storage.mjs +49 -0
  398. package/modules/release/README.md +212 -0
  399. package/modules/release/catalogue.mjs +79 -0
  400. package/modules/release/export.mjs +51 -0
  401. package/modules/release/produce.mjs +136 -0
  402. package/modules/release/public-child-transport-state.cjs +29 -0
  403. package/modules/release/public-child.mjs +71 -0
  404. package/modules/release/public-git.mjs +21 -0
  405. package/modules/release/publication-scan.mjs +22 -0
  406. package/modules/release/publish-handoff.mjs +37 -0
  407. package/modules/release/source.mjs +71 -0
  408. package/modules/release/test-summary.mjs +50 -0
  409. package/onboarding/README.md +163 -0
  410. package/onboarding/activity.mjs +31 -0
  411. package/onboarding/adapter-auth.mjs +88 -0
  412. package/onboarding/adapter-hosting.mjs +35 -0
  413. package/onboarding/adapter-labels.mjs +13 -0
  414. package/onboarding/adapter-settings.mjs +129 -0
  415. package/onboarding/adapter.mjs +238 -0
  416. package/onboarding/agent-handoff.mjs +9 -0
  417. package/onboarding/app-processes.mjs +21 -0
  418. package/onboarding/archive-output.d.mts +3 -0
  419. package/onboarding/archive-output.mjs +54 -0
  420. package/onboarding/command-custody.mjs +24 -0
  421. package/onboarding/command-recovery.mjs +8 -0
  422. package/onboarding/commands.mjs +166 -0
  423. package/onboarding/dependency-approval.mjs +36 -0
  424. package/onboarding/dev-identity.mjs +23 -0
  425. package/onboarding/email.mjs +24 -0
  426. package/onboarding/failures.mjs +42 -0
  427. package/onboarding/input-limits.mjs +7 -0
  428. package/onboarding/installer-inputs.mjs +24 -0
  429. package/onboarding/launcher.mjs +34 -0
  430. package/onboarding/local/build-files.mjs +64 -0
  431. package/onboarding/local/build.mjs +49 -0
  432. package/onboarding/local/environment.mjs +30 -0
  433. package/onboarding/local/files.mjs +35 -0
  434. package/onboarding/local/operation.mjs +166 -0
  435. package/onboarding/local/owner.mjs +34 -0
  436. package/onboarding/local/process-table.mjs +114 -0
  437. package/onboarding/local/recovery.mjs +79 -0
  438. package/onboarding/local/state.mjs +31 -0
  439. package/onboarding/metadata.mjs +7 -0
  440. package/onboarding/operator-install.mjs +45 -0
  441. package/onboarding/operator-installer.mjs +39 -0
  442. package/onboarding/operator-prerequisites.mjs +27 -0
  443. package/onboarding/package.json +6 -0
  444. package/onboarding/prefill.mjs +16 -0
  445. package/onboarding/private-files.mjs +97 -0
  446. package/onboarding/private.mjs +35 -0
  447. package/onboarding/process-cleanup.mjs +134 -0
  448. package/onboarding/process-diagnostics.mjs +91 -0
  449. package/onboarding/process-scope.mjs +118 -0
  450. package/onboarding/process-table.mjs +118 -0
  451. package/onboarding/process-tree.mjs +36 -0
  452. package/onboarding/production-modules.mjs +22 -0
  453. package/onboarding/production-options.mjs +40 -0
  454. package/onboarding/production-plan.mjs +8 -0
  455. package/onboarding/production-runtime.mjs +30 -0
  456. package/onboarding/project-vite.mjs +14 -0
  457. package/onboarding/providers.mjs +289 -0
  458. package/onboarding/queue.mjs +53 -0
  459. package/onboarding/queued-entry.mjs +34 -0
  460. package/onboarding/readiness.mjs +59 -0
  461. package/onboarding/redaction-jwt.mjs +32 -0
  462. package/onboarding/redaction.mjs +298 -0
  463. package/onboarding/schema/SETUP.schema.json +514 -0
  464. package/onboarding/schema/dependencies.mjs +35 -0
  465. package/onboarding/schema/json-schema.mjs +202 -0
  466. package/onboarding/schema/setup-validation.mjs +325 -0
  467. package/onboarding/secrets.mjs +21 -0
  468. package/onboarding/server.mjs +188 -0
  469. package/onboarding/setup-manifest.mjs +116 -0
  470. package/onboarding/state.mjs +46 -0
  471. package/onboarding/theme-files.mjs +37 -0
  472. package/onboarding/theme.mjs +118 -0
  473. package/onboarding/ui/activity.js +98 -0
  474. package/onboarding/ui/address-client.mjs +33 -0
  475. package/onboarding/ui/app.js +377 -0
  476. package/onboarding/ui/index.html +1 -0
  477. package/onboarding/ui/model-core.mjs +127 -0
  478. package/onboarding/ui/model.mjs +5 -0
  479. package/onboarding/ui/style.css +63 -0
  480. package/onboarding/ui/theme.css +37 -0
  481. package/onboarding/ui/workspace-art.js +30 -0
  482. package/onboarding/wrangler-auth.mjs +43 -0
  483. package/operations/README.md +246 -0
  484. package/operations/operations.mjs +168 -0
  485. package/operations/project-operations.mjs +157 -0
  486. package/package.json +11 -4
  487. package/scripts/publish-check/README.md +72 -0
  488. package/scripts/publish-check/checksum_maps.py +52 -0
  489. package/scripts/publish-check/cli.py +34 -0
  490. package/scripts/publish-check/command_arguments.py +51 -0
  491. package/scripts/publish-check/command_shell_quotes.py +41 -0
  492. package/scripts/publish-check/command_source_quotes.py +120 -0
  493. package/scripts/publish-check/content_identities.py +250 -0
  494. package/scripts/publish-check/copy_identities.py +41 -0
  495. package/scripts/publish-check/der.py +61 -0
  496. package/scripts/publish-check/destination.py +117 -0
  497. package/scripts/publish-check/env_values.py +74 -0
  498. package/scripts/publish-check/font_licence.py +23 -0
  499. package/scripts/publish-check/git_history.py +115 -0
  500. package/scripts/publish-check/github_refs.py +217 -0
  501. package/scripts/publish-check/gitleaks.py +72 -0
  502. package/scripts/publish-check/gitleaks.toml +40 -0
  503. package/scripts/publish-check/history_scan.py +176 -0
  504. package/scripts/publish-check/hit_location.py +18 -0
  505. package/scripts/publish-check/hygiene_config.py +42 -0
  506. package/scripts/publish-check/hygiene_text.py +11 -0
  507. package/scripts/publish-check/identity-recipe.mjs +54 -0
  508. package/scripts/publish-check/json_identity.py +79 -0
  509. package/scripts/publish-check/license-exceptions.json +11 -0
  510. package/scripts/publish-check/licenses.py +49 -0
  511. package/scripts/publish-check/policy.py +18 -0
  512. package/scripts/publish-check/project_identity.py +89 -0
  513. package/scripts/publish-check/provenance.py +91 -0
  514. package/scripts/publish-check/provenance_pins.py +33 -0
  515. package/scripts/publish-check/public_base.py +63 -0
  516. package/scripts/publish-check/public_hygiene.py +80 -0
  517. package/scripts/publish-check/publication-config.json +12 -0
  518. package/scripts/publish-check/publication_config.py +34 -0
  519. package/scripts/publish-check/report.py +52 -0
  520. package/scripts/publish-check/repository_commands.py +75 -0
  521. package/scripts/publish-check/repository_intent.py +52 -0
  522. package/scripts/publish-check/reviewed.py +41 -0
  523. package/scripts/publish-check/source_hashes.mjs +6 -0
  524. package/scripts/publish-check/tree_scan.py +88 -0
  525. package/scripts/publish-check/trust_bundle.py +87 -0
  526. package/scripts/publish-check.mjs +58 -0
@@ -0,0 +1,97 @@
1
+ // Per-checkout launch state, private to the account, beside the server
2
+ // registry: <servers>/launches/<name>-<hash>/
3
+ // worker.json the preparing worker (PID, start time, token) while it runs
4
+ // events-<token>.jsonl what that worker has done, one JSON event per line
5
+ // install.json whether an install this launcher started has finished
6
+ // setup.json whether a setup this launcher started has finished
7
+ // launch.log the worker's private log (child output)
8
+ // server.log the private log of the server rer started
9
+ // server.json that server's PID
10
+ import { createHash } from 'node:crypto';
11
+ import { appendFile, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
12
+ import { basename, join } from 'node:path';
13
+ import { processInfo } from '../../modules/job-queue/processes.mjs';
14
+ import { serversDirectory } from '../registry.mjs';
15
+
16
+ export function launchPath(checkout, env = process.env) {
17
+ const name = basename(checkout).toLowerCase().replace(/[^a-z0-9-]+/g, '-').slice(0, 40) || 'checkout';
18
+ return join(serversDirectory(env), 'launches', `${name}-${createHash('sha256').update(checkout).digest('hex').slice(0, 12)}`);
19
+ }
20
+
21
+ export async function launchDirectory(checkout, env = process.env) {
22
+ const directory = launchPath(checkout, env);
23
+ await mkdir(directory, { recursive: true, mode: 0o700 });
24
+ return directory;
25
+ }
26
+
27
+ async function readJson(path) { try { return JSON.parse(await readFile(path, 'utf8')); } catch (error) { if (error.code === 'ENOENT' || error instanceof SyntaxError) return undefined; throw error; } }
28
+
29
+ async function live(holder) {
30
+ if (!holder?.pid) return false;
31
+ const current = await processInfo(holder.pid);
32
+ return Boolean(current && !current.zombie && current.start === holder.start);
33
+ }
34
+
35
+ export async function liveWorker(directory) {
36
+ const holder = await readJson(join(directory, 'worker.json'));
37
+ return await live(holder) ? holder : undefined;
38
+ }
39
+
40
+ // A port chosen for a setup stays claimed while the process that chose it
41
+ // lives, so two launches preparing at once never pick the same port.
42
+ export async function claimPort(port, env = process.env) {
43
+ const directory = join(serversDirectory(env), 'launches', 'ports'), path = join(directory, `${port}.json`);
44
+ await mkdir(directory, { recursive: true, mode: 0o700 });
45
+ const { start } = await processInfo(process.pid);
46
+ for (let attempt = 0; attempt < 2; attempt++) {
47
+ try {
48
+ await writeFile(path, JSON.stringify({ pid: process.pid, start }), { flag: 'wx', mode: 0o600 });
49
+ return { release: async () => { if ((await readJson(path))?.pid === process.pid) await rm(path, { force: true }); } };
50
+ } catch (error) { if (error.code !== 'EEXIST') throw error; }
51
+ if (await live(await readJson(path))) return undefined;
52
+ await rm(path, { force: true });
53
+ }
54
+ return undefined;
55
+ }
56
+
57
+ // The worker claims the checkout's launch; a second worker started at the
58
+ // same moment finds the first and leaves.
59
+ export async function claimLaunch(directory, token) {
60
+ const path = join(directory, 'worker.json');
61
+ for (let attempt = 0; attempt < 3; attempt++) {
62
+ const { start } = await processInfo(process.pid);
63
+ try {
64
+ await writeFile(path, JSON.stringify({ token, pid: process.pid, start, startedAt: Date.now(), events: `events-${token}.jsonl` }), { flag: 'wx', mode: 0o600 });
65
+ for (const entry of await readdir(directory)) if (/^events-.*\.jsonl$/.test(entry) && entry !== `events-${token}.jsonl`) await rm(join(directory, entry), { force: true });
66
+ return true;
67
+ } catch (error) {
68
+ if (error.code !== 'EEXIST') throw error;
69
+ if (await liveWorker(directory)) return false;
70
+ await rm(path, { force: true });
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+
76
+ export async function releaseLaunch(directory, token) {
77
+ const holder = await readJson(join(directory, 'worker.json'));
78
+ if (holder?.token === token) await rm(join(directory, 'worker.json'), { force: true });
79
+ }
80
+
81
+ export function eventLog(directory, token) {
82
+ const path = join(directory, `events-${token}.jsonl`);
83
+ return { path, write: event => appendFile(path, JSON.stringify({ at: Date.now(), ...event }) + '\n', { mode: 0o600 }) };
84
+ }
85
+
86
+ // { state: 'running' | 'done' | 'failed', ... } for the last install or setup
87
+ // this launcher ran. 'running' survives only a step whose process was killed.
88
+ const stepRecord = name => ({
89
+ read: directory => readJson(join(directory, `${name}.json`)),
90
+ write: (directory, record) => writeFile(join(directory, `${name}.json`), JSON.stringify({ ...record, at: Date.now() }), { mode: 0o600 }),
91
+ });
92
+ export const installState = stepRecord('install'), setupState = stepRecord('setup');
93
+
94
+ export const serverRecord = {
95
+ read: directory => readJson(join(directory, 'server.json')),
96
+ write: (directory, record) => writeFile(join(directory, 'server.json'), JSON.stringify(record), { mode: 0o600 }),
97
+ };
@@ -0,0 +1,81 @@
1
+ // Run the pinned dispatcher's public verbs as children, reading its --json
2
+ // events: install and setup to completion, and dev until its `ready` event.
3
+ import { spawn } from 'node:child_process';
4
+ import { createReadStream } from 'node:fs';
5
+ import { open, stat } from 'node:fs/promises';
6
+ import { createInterface } from 'node:readline';
7
+ import { setTimeout as delay } from 'node:timers/promises';
8
+ import { LaunchFailure } from './failure.mjs';
9
+
10
+ function events(line) { try { const value = JSON.parse(line); return value && typeof value === 'object' ? value : undefined; } catch { return undefined; } }
11
+
12
+ // What a child's JSON event means for the person waiting.
13
+ export function waitingNote(event) {
14
+ if (event.event === 'waiting') return `waiting in the job queue${event.reason ? `: ${event.reason}` : ''}`;
15
+ if (event.event === 'node-selection') return event.message;
16
+ if (event.event === 'port') return `using port ${event.port}`;
17
+ return undefined;
18
+ }
19
+
20
+ export function failureFrom(code, error, output) {
21
+ return new LaunchFailure(code || 1, error?.message ?? (output.trim().split('\n').slice(-5).join('\n') || `The command stopped with exit ${code}.`), error);
22
+ }
23
+
24
+ // One bounded verb, never prompting: no stdin, --non-interactive, --json.
25
+ export async function runVerb(entry, args, { root, env, note, log, children }) {
26
+ const child = spawn(process.execPath, [entry, ...args, '--json', '--non-interactive'], { cwd: root, env, stdio: ['ignore', 'pipe', 'pipe'] });
27
+ children.add(child);
28
+ let error, output = '';
29
+ const lines = createInterface({ input: child.stdout });
30
+ lines.on('line', line => { const event = events(line); if (event?.event === 'error') error = event; const text = event && waitingNote(event); if (text) note(text); log(line); });
31
+ child.stderr.on('data', data => { output = (output + data).slice(-8192); log(String(data).trimEnd()); });
32
+ const code = await new Promise((resolve, reject) => { child.once('error', reject); child.once('close', (exit, signal) => resolve(exit ?? (signal ? 128 : 1))); });
33
+ children.delete(child);
34
+ if (code !== 0) throw failureFrom(code, error, output);
35
+ }
36
+
37
+ // Follow a growing log file from an offset, line by line.
38
+ async function follow(path, from, onLine, stopped) {
39
+ let offset = from, rest = '';
40
+ while (!stopped()) {
41
+ const size = (await stat(path)).size;
42
+ if (size > offset) {
43
+ const handle = await open(path, 'r');
44
+ try {
45
+ const { buffer, bytesRead } = await handle.read({ buffer: Buffer.alloc(size - offset), position: offset });
46
+ offset += bytesRead; rest += buffer.subarray(0, bytesRead).toString('utf8');
47
+ } finally { await handle.close(); }
48
+ const parts = rest.split('\n'); rest = parts.pop();
49
+ for (const line of parts) if (onLine(line)) return;
50
+ }
51
+ await delay(200);
52
+ }
53
+ }
54
+
55
+ // Start `dev` detached into its own process group with its output in a private
56
+ // log, and return its ready event once it prints one. The server outlives this
57
+ // process and stops on rer dev --stop or its idle timeout.
58
+ export async function startServer(entry, args, { root, env, logPath, children }) {
59
+ const handle = await open(logPath, 'a', 0o600), from = (await handle.stat()).size;
60
+ let child;
61
+ try { child = spawn(process.execPath, [entry, 'dev', ...args, '--json'], { cwd: root, env, detached: true, stdio: ['ignore', handle.fd, handle.fd] }); }
62
+ finally { await handle.close(); }
63
+ children.add(child);
64
+ let exit, ready, error, output = '';
65
+ child.once('exit', (code, signal) => { exit = code ?? (signal ? 128 : 1); });
66
+ await follow(logPath, from, line => {
67
+ const event = events(line);
68
+ if (event?.event === 'ready') { ready = event; return true; }
69
+ if (event?.event === 'error') error = event;
70
+ if (!event) output = (output + line + '\n').slice(-8192);
71
+ return false;
72
+ }, () => exit !== undefined);
73
+ if (!ready) {
74
+ // Read whatever the exiting dispatcher wrote last.
75
+ for await (const line of createInterface({ input: createReadStream(logPath, { start: from }) })) { const event = events(line); if (event?.event === 'ready') ready = event; if (event?.event === 'error') error = event; }
76
+ }
77
+ children.delete(child);
78
+ if (!ready) throw failureFrom(exit, error, output);
79
+ child.unref();
80
+ return { ready, pid: child.pid, reused: exit === 0 };
81
+ }
@@ -0,0 +1,43 @@
1
+ // The detached launch worker: node worker.mjs ROOT TOKEN ARGS_JSON
2
+ // It runs in its own session, so killing the `rer dev` that started it (or that
3
+ // command's whole process group) leaves it running; a rerun attaches to it.
4
+ import { createWriteStream } from 'node:fs';
5
+ import { realpath } from 'node:fs/promises';
6
+ import { join } from 'node:path';
7
+ import { liveServers } from '../registry.mjs';
8
+ import { prepare } from './prepare.mjs';
9
+ import { claimLaunch, eventLog, launchDirectory, liveWorker, releaseLaunch, serverRecord } from './state.mjs';
10
+ import { startServer } from './steps.mjs';
11
+
12
+ const [root, token, argsJSON] = process.argv.slice(2), args = JSON.parse(argsJSON);
13
+ const checkout = await realpath(root), directory = await launchDirectory(checkout), events = eventLog(directory, token);
14
+ if (!await claimLaunch(directory, token)) {
15
+ const holder = await liveWorker(directory);
16
+ await events.write({ event: 'redirect', token: holder?.token });
17
+ } else {
18
+ const logFile = createWriteStream(join(directory, 'launch.log'), { flags: 'a', mode: 0o600 });
19
+ const log = text => logFile.write(text + '\n');
20
+ const children = new Set();
21
+ for (const signal of ['SIGTERM', 'SIGINT']) process.on(signal, () => { for (const child of children) child.kill('SIGTERM'); });
22
+ process.on('SIGHUP', () => {});
23
+ try {
24
+ const prepared = await prepare({ root, args, env: process.env, report: fields => events.write({ event: 'phase', ...fields }), directory, log, children });
25
+ await events.write({ event: 'phase', phase: 'start', message: prepared.own ? 'the app is already running; checking it' : 'starting the app' });
26
+ // A server rer started logs to server.log; asking a running one for its
27
+ // ready lines goes to reuse.log, and names server.log only when rer started it.
28
+ const serverLog = join(directory, 'server.log');
29
+ const started = await startServer(prepared.entry, args, { root, env: { ...process.env, ...prepared.added }, logPath: prepared.own ? join(directory, 'reuse.log') : serverLog, children });
30
+ const record = (await liveServers(process.env)).find(item => item.checkout === checkout);
31
+ await prepared.release?.();
32
+ const pid = record?.owner?.pid ?? started.pid;
33
+ if (!prepared.own) await serverRecord.write(directory, { pid });
34
+ const ours = !prepared.own || (await serverRecord.read(directory))?.pid === pid;
35
+ await events.write({ event: 'ready', ready: started.ready, pid, ...(ours ? { log: serverLog } : {}) });
36
+ } catch (error) {
37
+ log(String(error?.stack ?? error));
38
+ await events.write({ event: 'failed', exitCode: error.exitCode ?? 1, message: error.exitCode ? error.message : `The launch failed unexpectedly; see ${join(directory, 'launch.log')}.`, ...(error.event ? { error: error.event } : {}) });
39
+ } finally {
40
+ await releaseLaunch(directory, token);
41
+ logFile.end();
42
+ }
43
+ }
@@ -0,0 +1,59 @@
1
+ // rer list [DIR] [--json]: the immediate subfolders of DIR that hold an
2
+ // OPERATIONS.json, newest change first, with what rer dev can do for each.
3
+ import { execFile } from 'node:child_process';
4
+ import { readdir, realpath, stat } from 'node:fs/promises';
5
+ import { join, resolve } from 'node:path';
6
+ import { promisify } from 'node:util';
7
+ import { descriptorProfile, dispatcher } from './checkout.mjs';
8
+ import { liveServers } from './registry.mjs';
9
+ import { describeCheckout, setupNeeded } from './launch/facts.mjs';
10
+ import { launchPath, setupState } from './launch/state.mjs';
11
+
12
+ const run = promisify(execFile);
13
+ const reasons = {
14
+ independent: 'independent project: rer runs its declared commands; zero-setup rer dev applies to first-party seeds only',
15
+ unreadable: 'OPERATIONS.json cannot be read',
16
+ };
17
+
18
+ async function changed(path) {
19
+ try { const { stdout } = await run('git', ['-C', path, 'log', '-1', '--format=%ct'], { timeout: 10000 }); if (/^\d+$/.test(stdout.trim())) return Number(stdout.trim()) * 1000; } catch {}
20
+ return (await stat(path)).mtimeMs;
21
+ }
22
+
23
+ async function entry(path, running) {
24
+ const facts = descriptorProfile(join(path, 'OPERATIONS.json'));
25
+ const profile = ['first-party', 'independent'].includes(facts.profile) ? facts.profile : 'unsupported';
26
+ const result = { path, name: path.split('/').pop(), profile, launchable: profile === 'first-party' };
27
+ if (!result.launchable) result.reason = reasons[facts.profile] ?? `OPERATIONS.json is version ${facts.version ?? '(none)'}, which rer does not run`;
28
+ result.prepared = null;
29
+ if (profile === 'first-party') {
30
+ // Set up: saved settings, and no setup a launch started left unfinished.
31
+ try { result.prepared = !await setupNeeded(path, await describeCheckout(path, dispatcher(path).entry, process.env), await setupState.read(launchPath(path))); }
32
+ catch (failure) { result.launchable = false; result.reason = `rer describe fails here: ${failure.message.split('\n')[0]}`; }
33
+ }
34
+ result.url = running.get(path) ?? null;
35
+ result.changedAt = new Date(await changed(path)).toISOString();
36
+ return result;
37
+ }
38
+
39
+ export async function listCommand(args) {
40
+ const json = args.includes('--json'), positional = args.filter(arg => arg !== '--json');
41
+ if (positional.length > 1 || positional.some(arg => arg.startsWith('-'))) { process.stderr.write('Usage: rer list [DIR] [--json]\n'); return 2; }
42
+ let directory;
43
+ try { directory = await realpath(resolve(positional[0] ?? '.')); } catch { process.stderr.write(`rer list: ${resolve(positional[0] ?? '.')} is not a folder.\n`); return 3; }
44
+ const running = new Map((await liveServers()).map(record => [record.checkout, record.url]));
45
+ const folders = [];
46
+ for (const item of await readdir(directory, { withFileTypes: true })) {
47
+ const path = join(directory, item.name);
48
+ try { if ((await stat(path)).isDirectory() && (await stat(join(path, 'OPERATIONS.json'))).isFile()) folders.push(await realpath(path)); } catch {}
49
+ }
50
+ const checkouts = [];
51
+ for (let i = 0; i < folders.length; i += 4) checkouts.push(...await Promise.all(folders.slice(i, i + 4).map(path => entry(path, running))));
52
+ checkouts.sort((a, b) => b.changedAt.localeCompare(a.changedAt) || a.name.localeCompare(b.name));
53
+ if (json) { process.stdout.write(JSON.stringify({ directory, checkouts }) + '\n'); return 0; }
54
+ if (!checkouts.length) { process.stdout.write(`No checkouts with an OPERATIONS.json in ${directory}.\n`); return 0; }
55
+ const width = Math.max(4, ...checkouts.map(item => item.name.length));
56
+ const rows = checkouts.map(item => `${item.name.padEnd(width)} ${item.profile.padEnd(11)} ${(item.launchable ? 'yes' : 'no').padEnd(6)} ${(item.prepared === null ? '-' : item.prepared ? 'yes' : 'no').padEnd(8)} ${item.url ?? (item.launchable ? '-' : item.reason)}`);
57
+ process.stdout.write([`${'NAME'.padEnd(width)} ${'PROFILE'.padEnd(11)} LAUNCH PREPARED URL`, ...rows, '', 'Launch one with: rer dev --cwd FOLDER'].join('\n') + '\n');
58
+ return 0;
59
+ }
@@ -0,0 +1,53 @@
1
+ import { dispatcher, locate, unsupportedMessage } from './checkout.mjs';
2
+ import { delegate } from './delegate.mjs';
3
+ import { localDefaults } from './launch/local-defaults.mjs';
4
+
5
+ const help = `Usage: rer VERB [FLAGS]
6
+
7
+ Inside a project (the nearest OPERATIONS.json at or above this folder, or --cwd DIR),
8
+ every verb runs that checkout's own pinned rer, exactly as node scripts/rer.mjs VERB would.
9
+
10
+ rer dev Launch a first-party checkout with no setup steps: installs dependencies,
11
+ sets up a local owner and a free port when needed, starts the app and prints
12
+ RER_READY and a sign-in link. Without a terminal it returns once the app is
13
+ ready and leaves it running (--detach and --foreground override); allow up
14
+ to 10 minutes on a first launch, or rerun it to reattach. rer dev --stop stops it.
15
+ rer list [DIR] The checkouts in DIR (default: here), newest first, with whether rer dev can
16
+ launch them, whether they are set up, and their running URL. --json for data.
17
+ rer cli status This rer: version, contents digest, install channel, launcher and other rer
18
+ commands on PATH. --json for data.
19
+ rer cli install Install this package for the current user (~/.local/bin/rer).
20
+ rer cli uninstall Remove the launcher and the install root; ~/.rer is untouched.
21
+ rer update Move to the latest published rer (--version X for an exact one), the way it
22
+ was installed: npx and install.sh installs switch atomically, npm installs run npm.
23
+ rer --version Print this rer's version; inside a checkout, also the version the checkout pins.
24
+
25
+ Checkout verbs: install, setup, dev, preview, build, verify, reset, deploy, export, import,
26
+ outbox, doctor, describe, check, release. Run rer VERB --help in a checkout for its flags.
27
+ `;
28
+
29
+ export async function main(argv) {
30
+ const [verb] = argv;
31
+ if (verb === '--version' || verb === '-v') return (await import('./version.mjs')).versionCommand(argv);
32
+ // Only the zero-setup launch sets this, for its own setup child; an explicit
33
+ // verb never inherits it.
34
+ delete process.env[localDefaults];
35
+ if (verb === 'update') return (await import('./update/command.mjs')).update(argv.slice(1));
36
+ const notice = await import('./update/notice.mjs');
37
+ if (notice.wantsNotice(argv)) await notice.updateNotice();
38
+ if (!verb || verb === '--help' || verb === '-h' || verb === 'help') { process.stdout.write(help); return 0; }
39
+ if (verb === 'cli') return (await import('./cli/command.mjs')).cliCommand(argv.slice(1));
40
+ if (verb === 'list') return (await import('./list.mjs')).listCommand(argv.slice(1));
41
+ const target = locate(argv);
42
+ if (target.profile === 'unsupported') { process.stderr.write(unsupportedMessage(target.root, target.version) + '\n'); return 2; }
43
+ // A checkout that pins its own rer package runs all of it, launching included.
44
+ const mode = target.profile === 'first-party' ? dispatcher(target.root).mode : undefined;
45
+ if (verb === 'dev' && mode && mode !== 'package' && !argv.includes('--help')) return (await import('./launch/dev.mjs')).launchDev(target);
46
+ // Every other server start or queued job on a vendored pin gets the person
47
+ // defaults too (a pinned package applies its own).
48
+ if (mode === 'vendored') {
49
+ const { personEnvironment, personVerbs, pinPolicy } = await import('./launch/capacity.mjs');
50
+ if (personVerbs.has(verb)) Object.assign(process.env, personEnvironment(process.env, target.args, await pinPolicy(target.root, mode)));
51
+ }
52
+ await delegate(target, target.args);
53
+ }
@@ -0,0 +1,38 @@
1
+ // Facts about the running rer package: where it lives, its version and the
2
+ // digest of its contents (which tells two builds of one version apart).
3
+ import { createHash } from 'node:crypto';
4
+ import { readFile, readdir } from 'node:fs/promises';
5
+ import { readFileSync, realpathSync } from 'node:fs';
6
+ import { join, relative } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ export const packageName = '@runeditrun/rer';
10
+ export const ownRoot = realpathSync(fileURLToPath(new URL('../', import.meta.url)));
11
+
12
+ export function ownManifest() {
13
+ const packed = JSON.parse(readFileSync(join(ownRoot, 'package.json'), 'utf8'));
14
+ return packed.name === packageName ? packed : JSON.parse(readFileSync(join(ownRoot, 'global/package.json'), 'utf8'));
15
+ }
16
+
17
+ export async function packageFileList(root) {
18
+ const files = [];
19
+ async function walk(directory) {
20
+ for (const entry of (await readdir(directory, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
21
+ const path = join(directory, entry.name);
22
+ if (entry.isDirectory()) await walk(path);
23
+ else if (entry.isFile()) files.push(relative(root, path));
24
+ else throw Error(`Unexpected non-file entry in the rer package: ${relative(root, path)}`);
25
+ }
26
+ }
27
+ await walk(root);
28
+ return files.sort();
29
+ }
30
+
31
+ // sha256 over every file's path and bytes, in a fixed order.
32
+ export async function packageDigest(root) {
33
+ const hash = createHash('sha256');
34
+ for (const path of await packageFileList(root)) {
35
+ hash.update(path).update('\0').update(await readFile(join(root, path))).update('\0');
36
+ }
37
+ return hash.digest('hex');
38
+ }
@@ -0,0 +1,24 @@
1
+ // Read-only view of the shared server registry that every pinned dispatcher
2
+ // writes (RER_SERVERS_DIR, else the account's ~/.rer/servers).
3
+ import { readFile } from 'node:fs/promises';
4
+ import { userInfo } from 'node:os';
5
+ import { join, resolve } from 'node:path';
6
+ import { alive } from '../modules/job-queue/identity.mjs';
7
+
8
+ export const serversDirectory = (env = process.env) => resolve(env.RER_SERVERS_DIR ?? join(userInfo().homedir, '.rer/servers'));
9
+
10
+ export async function liveServers(env = process.env) {
11
+ let state;
12
+ try { state = JSON.parse(await readFile(join(serversDirectory(env), 'state.json'), 'utf8')); }
13
+ catch (error) { if (error.code === 'ENOENT') return []; throw error; }
14
+ const live = [];
15
+ for (const record of state.running ?? []) {
16
+ // An identity that cannot be verified is kept, as the dispatchers keep it.
17
+ let running = true;
18
+ try { running = await alive(record.owner); } catch {}
19
+ if (running) live.push(record);
20
+ }
21
+ return live;
22
+ }
23
+
24
+ export const stopCommand = record => `rer ${record.mode} --stop --cwd ${JSON.stringify(record.checkout)}`;
package/global/rer.mjs ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // The installed `rer` command. This file avoids newer syntax so that an old
3
+ // Node still reaches the one-line minimum-version message.
4
+ import { readFileSync } from 'node:fs';
5
+
6
+ function manifest() {
7
+ // Packed: the package's own package.json. From a Base checkout: the template.
8
+ var packed = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
9
+ return packed.name === '@runeditrun/rer' ? packed : JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
10
+ }
11
+
12
+ function below(actual, minimum) {
13
+ var a = actual.split('.').map(Number), m = minimum.split('.').map(Number);
14
+ for (var i = 0; i < 3; i++) { if ((a[i] || 0) !== (m[i] || 0)) return (a[i] || 0) < (m[i] || 0); }
15
+ return false;
16
+ }
17
+
18
+ var minimum = manifest().engines.node.replace(/^>=\s*/, '');
19
+ if (process.platform === 'win32') {
20
+ process.stderr.write('rer runs on macOS and Linux. On Windows, install it inside WSL2.\n');
21
+ process.exitCode = 1;
22
+ } else if (below(process.versions.node, minimum)) {
23
+ process.stderr.write('rer needs Node ' + minimum + ' or later; this is Node ' + process.versions.node + '. Install a newer Node, then run rer again.\n');
24
+ process.exitCode = 1;
25
+ } else {
26
+ import('./main.mjs').then(function (module) { return module.main(process.argv.slice(2)); }).then(function (code) {
27
+ if (code !== undefined) process.exitCode = code;
28
+ });
29
+ }
@@ -0,0 +1,89 @@
1
+ // Read an npm tarball for installing: check it is exactly the registry's bytes
2
+ // (sha512 integrity, and the entry count and unpacked size when the registry
3
+ // gives them) and that every entry is a plain file or folder under package/,
4
+ // then write it out. Absolute paths, .. entries, links and devices are refused,
5
+ // and so is anything another tar reader could read differently: a format other
6
+ // than ustar, a pax extended header with a field other than path, size or
7
+ // mtime, or data after the end of the archive. install.sh checks the same way.
8
+ import { createHash } from 'node:crypto';
9
+ import { mkdir, writeFile } from 'node:fs/promises';
10
+ import { dirname, join } from 'node:path';
11
+ import { gunzipSync } from 'node:zlib';
12
+ import { UpdateFailure } from './registry.mjs';
13
+
14
+ const text = (block, start, length) => { const end = block.subarray(start, start + length).indexOf(0); return block.toString('utf8', start, end === -1 ? start + length : start + end); };
15
+
16
+ // A pax extended header's records ("<length> <key>=<value>\n"): path and size
17
+ // replace the next entry's own, mtime is only a time; any other field is refused.
18
+ function paxFields(data) {
19
+ const fields = {};
20
+ for (let offset = 0; offset < data.length;) {
21
+ const space = data.indexOf(0x20, offset), digits = data.toString('latin1', offset, space), length = Number(digits);
22
+ if (space === -1 || !/^[1-9]\d*$/.test(digits) || offset + length <= space + 1 || offset + length > data.length || data[offset + length - 1] !== 0x0a) throw new UpdateFailure('The archive has a damaged extended header.');
23
+ const record = data.toString('utf8', space + 1, offset + length - 1), equals = record.indexOf('=');
24
+ const key = record.slice(0, equals), value = record.slice(equals + 1);
25
+ offset += length;
26
+ if (equals < 1) throw new UpdateFailure('The archive has a damaged extended header.');
27
+ if (key === 'linkpath') throw new UpdateFailure('The archive contains a link.');
28
+ if (key === 'size' && !/^\d+$/.test(value)) throw new UpdateFailure('The archive has a damaged extended header.');
29
+ if (key !== 'path' && key !== 'size' && key !== 'mtime') throw new UpdateFailure(`The archive has an extended header field rer does not read (${key}).`);
30
+ fields[key] = key === 'size' ? Number(value) : value;
31
+ }
32
+ return fields;
33
+ }
34
+
35
+ // Every entry under package/, folders included (each counts toward the
36
+ // registry's dist.fileCount); a folder has empty data.
37
+ export function readPackage(bytes, dist) {
38
+ if (!/^sha512-[A-Za-z0-9+/]+=*$/.test(dist.integrity ?? '')) throw new UpdateFailure('The registry gives no sha512 integrity for this version.');
39
+ if (`sha512-${createHash('sha512').update(bytes).digest('base64')}` !== dist.integrity) throw new UpdateFailure('The download does not match the registry\'s sha512 integrity.');
40
+ let tar;
41
+ try { tar = gunzipSync(bytes); } catch { throw new UpdateFailure('The download is not a gzip archive.'); }
42
+ const entries = [];
43
+ let offset = 0, pax, ended = false;
44
+ while (offset + 512 <= tar.length) {
45
+ const block = tar.subarray(offset, offset + 512);
46
+ if (block.every(byte => byte === 0)) {
47
+ if (!tar.subarray(offset).every(byte => byte === 0)) throw new UpdateFailure('The archive has data after its end.');
48
+ ended = true;
49
+ break;
50
+ }
51
+ let sum = 0;
52
+ for (let i = 0; i < 512; i++) sum += i >= 148 && i < 156 ? 32 : block[i];
53
+ if (parseInt(text(block, 148, 8), 8) !== sum) throw new UpdateFailure('The archive has a damaged header.');
54
+ if (block.toString('latin1', 257, 265) !== 'ustar\u0000' + '00') throw new UpdateFailure('The archive is not a ustar archive.');
55
+ const sizeField = text(block, 124, 12).trim();
56
+ if (!/^[0-7]+$/.test(sizeField)) throw new UpdateFailure('The archive has a damaged header.');
57
+ const type = String.fromCharCode(block[156] || 48), size = type !== 'x' && pax?.size !== undefined ? pax.size : parseInt(sizeField, 8);
58
+ const data = tar.subarray(offset + 512, offset + 512 + size);
59
+ if (data.length < size) throw new UpdateFailure('The archive is truncated.');
60
+ offset += 512 + Math.ceil(size / 512) * 512;
61
+ if (type === 'x') {
62
+ if (pax) throw new UpdateFailure('The archive has two extended headers for one entry.');
63
+ pax = paxFields(data);
64
+ continue;
65
+ }
66
+ const name = pax?.path ?? (text(block, 345, 155) ? `${text(block, 345, 155)}/${text(block, 0, 100)}` : text(block, 0, 100));
67
+ pax = undefined;
68
+ if (type !== '0' && type !== '5') throw new UpdateFailure(`The archive contains a link, device or other special entry (${name}).`);
69
+ const parts = name.split('/');
70
+ if (name.startsWith('/') || parts.includes('..') || name.includes('\\') || parts[0] !== 'package') throw new UpdateFailure(`The archive contains an unsafe path (${name}).`);
71
+ if (type === '5' && size !== 0) throw new UpdateFailure(`The archive gives the folder ${name} contents.`);
72
+ if (type === '0' && name.endsWith('/')) throw new UpdateFailure(`The archive gives the file ${name} a folder's name.`);
73
+ entries.push({ path: parts.slice(1).filter(Boolean).join('/'), data, executable: (parseInt(text(block, 100, 8), 8) & 0o100) !== 0, directory: type === '5' });
74
+ }
75
+ if (!ended || pax) throw new UpdateFailure('The archive is truncated.');
76
+ if (dist.fileCount !== undefined && dist.fileCount !== entries.length) throw new UpdateFailure(`The archive holds ${entries.length} entries; the registry says ${dist.fileCount}.`);
77
+ const total = entries.reduce((sum, entry) => sum + entry.data.length, 0);
78
+ if (dist.unpackedSize !== undefined && dist.unpackedSize !== total) throw new UpdateFailure(`The archive unpacks to ${total} bytes; the registry says ${dist.unpackedSize}.`);
79
+ return entries;
80
+ }
81
+
82
+ export async function writePackage(entries, root) {
83
+ for (const entry of entries) {
84
+ if (entry.directory) { await mkdir(join(root, entry.path), { recursive: true }); continue; }
85
+ await mkdir(dirname(join(root, entry.path)), { recursive: true });
86
+ try { await writeFile(join(root, entry.path), entry.data, { mode: entry.executable ? 0o755 : 0o644, flag: 'wx' }); }
87
+ catch (error) { if (['EEXIST', 'EISDIR'].includes(error.code)) throw new UpdateFailure(`The archive holds ${entry.path} twice.`); throw error; }
88
+ }
89
+ }
@@ -0,0 +1,90 @@
1
+ // rer update [--version X]: move this rer to the registry's latest version (or
2
+ // X), the way it was installed. A user install (npx or install.sh) downloads,
3
+ // verifies and switches atomically through the new package's own
4
+ // `rer cli install`, keeping its channel; a failed or mismatched download
5
+ // leaves the current version selected. An npm global install runs npm for its
6
+ // own prefix and never writes a second launcher.
7
+ import { spawn } from 'node:child_process';
8
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
9
+ import { tmpdir } from 'node:os';
10
+ import { basename, dirname, join } from 'node:path';
11
+ import { layout } from '../cli/layout.mjs';
12
+ import { channelOf } from '../cli/status.mjs';
13
+ import { ownManifest, ownRoot, packageDigest, packageName } from '../package-info.mjs';
14
+ import { readPackage, writePackage } from './archive.mjs';
15
+ import { compareVersions, packageMetadata, pickRelease, registryUrl, UpdateFailure } from './registry.mjs';
16
+
17
+ const usage = 'Usage: rer update [--version X]\n';
18
+
19
+ function parse(args) {
20
+ if (!args.length) return {};
21
+ if (args.length === 1 && args[0].startsWith('--version=') && args[0].length > 10) return { version: args[0].slice(10) };
22
+ if (args.length === 2 && args[0] === '--version' && args[1] && !args[1].startsWith('-')) return { version: args[1] };
23
+ return undefined;
24
+ }
25
+
26
+ const run = (program, args, env = process.env) => new Promise((resolve, reject) => {
27
+ const child = spawn(program, args, { env, stdio: ['ignore', 'inherit', 'inherit'] });
28
+ child.once('error', reject);
29
+ child.once('close', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
30
+ });
31
+
32
+ // <prefix>/lib/node_modules/@runeditrun/rer, npm's global layout on macOS and Linux.
33
+ function npmPrefix() {
34
+ const scope = dirname(ownRoot), modules = dirname(scope), lib = dirname(modules);
35
+ if (basename(scope) !== '@runeditrun' || basename(modules) !== 'node_modules' || basename(lib) !== 'lib') throw new UpdateFailure(`This npm global rer is at ${ownRoot}, outside npm's usual layout; update it with npm install -g ${packageName}.`);
36
+ return dirname(lib);
37
+ }
38
+
39
+ async function download(release) {
40
+ const response = await fetch(release.dist.tarball).catch(error => { throw new UpdateFailure(`Could not download ${release.dist.tarball} (${error.cause?.code ?? error.message}).`); });
41
+ if (!response.ok) throw new UpdateFailure(`${release.dist.tarball} answered ${response.status}.`);
42
+ return readPackage(Buffer.from(await response.arrayBuffer()), release.dist);
43
+ }
44
+
45
+ async function npmGlobal(version) {
46
+ const prefix = npmPrefix(), args = ['install', '-g', '--prefix', prefix, `${packageName}@${version}`];
47
+ const command = `npm ${args.map(arg => /^[\w@./:=-]+$/.test(arg) ? arg : JSON.stringify(arg)).join(' ')}`;
48
+ process.stdout.write(`Updating the npm global rer: ${command}\n`);
49
+ let code;
50
+ try { code = await run('npm', args); } catch (error) { code = 127; process.stderr.write(`Could not run npm (${error.code ?? error.message}).\n`); }
51
+ if (code !== 0) {
52
+ process.stderr.write(`npm could not update rer (exit ${code}); rer is unchanged. Fix the problem npm reports (for example, a prefix you cannot write), then run:\n ${command}\n`);
53
+ return code || 1;
54
+ }
55
+ const installed = JSON.parse(await readFile(join(prefix, 'lib/node_modules', packageName, 'package.json'), 'utf8')).version;
56
+ process.stdout.write(`rer is now ${installed} (npm global, prefix ${prefix}).\n`);
57
+ return 0;
58
+ }
59
+
60
+ export async function update(args) {
61
+ const options = parse(args);
62
+ if (!options) { process.stderr.write(`Unsupported: rer update ${args.join(' ')}\n${usage}`); return 2; }
63
+ const { channel } = await channelOf(layout()), current = ownManifest().version;
64
+ if (channel === 'source') { process.stderr.write(`This rer runs from a source checkout (${ownRoot}), which rer update does not change. Update the checkout with git, or install rer with: npx -y ${packageName} cli install\n`); return 1; }
65
+ const work = await mkdtemp(join(tmpdir(), 'rer-update-'));
66
+ try {
67
+ const release = pickRelease(await packageMetadata(), options.version);
68
+ if (!options.version && compareVersions(current, release.version) > 0) {
69
+ process.stdout.write(`rer ${current} is newer than the latest published version (${release.version}); nothing changed. rer update --version ${release.version} installs that one.\n`);
70
+ return 0;
71
+ }
72
+ // The same version counts as current only with the same contents: a
73
+ // different build of that version is an update. (A development build's
74
+ // version differs from its release's, so it always moves.)
75
+ if (release.version === current || channel !== 'npm-global') {
76
+ process.stdout.write(`Checking rer ${release.version} from ${registryUrl()}\n`);
77
+ await writePackage(await download(release), join(work, 'package'));
78
+ if (release.version === current && await packageDigest(join(work, 'package')) === await packageDigest(ownRoot)) {
79
+ process.stdout.write(`rer ${current} is already current. Nothing changed.\n`);
80
+ return 0;
81
+ }
82
+ }
83
+ if (channel === 'npm-global') return await npmGlobal(release.version);
84
+ return await run(process.execPath, [join(work, 'package/global/rer.mjs'), 'cli', 'install'], { ...process.env, RER_INSTALL_CHANNEL: channel });
85
+ } catch (error) {
86
+ if (!(error instanceof UpdateFailure)) throw error;
87
+ process.stderr.write(`rer update: ${error.message} rer ${current} is still selected.\n`);
88
+ return 1;
89
+ } finally { await rm(work, { recursive: true, force: true }); }
90
+ }
@@ -0,0 +1,6 @@
1
+ // Prints the registry's latest @runeditrun/rer version on one line. The update
2
+ // notice runs this as a child process so that it can kill it at its time budget.
3
+ import { packageMetadata } from './registry.mjs';
4
+
5
+ const latest = (await packageMetadata())['dist-tags']?.latest;
6
+ if (typeof latest === 'string') process.stdout.write(latest + '\n');