@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,202 @@
1
+ /** Browser comparison is restricted to the JSON values carried by a setup manifest. */
2
+ function isDeepStrictEqual(left, right) {
3
+ if (Object.is(left, right)) return true;
4
+ if (!left || !right || typeof left !== 'object' || typeof right !== 'object'
5
+ || Array.isArray(left) !== Array.isArray(right)) return false;
6
+ const names = Object.keys(left);
7
+ return names.length === Object.keys(right).length
8
+ && names.every(name => Object.hasOwn(right, name) && isDeepStrictEqual(left[name], right[name]));
9
+ }
10
+
11
+ // Validates data against the subset of JSON Schema 2020-12 the seed schemas use.
12
+ //
13
+ // `checkSchema` walks a whole schema — every subschema, including branches no
14
+ // document reaches — and throws on the first keyword this file does not enforce,
15
+ // so a schema can never claim a rule that is not checked. Run it once before
16
+ // validating anything. `validate` repeats the check for the subschemas a given
17
+ // document reaches, which is all it can see on its own.
18
+ //
19
+ // A keyword added to SUPPORTED must be enforced in `validate`, and, if its value
20
+ // holds subschemas, listed in SUBSCHEMAS so the walk descends into it. A type
21
+ // added to TYPES must be one `typeOf` can return.
22
+
23
+ const SUPPORTED = new Set([
24
+ '$schema', '$id', '$ref', '$defs', 'title', 'description',
25
+ 'type', 'enum', 'const', 'required', 'properties', 'additionalProperties',
26
+ 'propertyNames', 'items', 'minItems', 'minLength', 'pattern', 'format',
27
+ 'allOf', 'if', 'then', 'uniqueItems'
28
+ ]);
29
+
30
+ // The supported keywords whose value holds subschemas, and how they hold them:
31
+ // 'schema' one, 'map' one per property name, 'list' one per array entry.
32
+ const SUBSCHEMAS = {
33
+ $defs: 'map',
34
+ properties: 'map',
35
+ additionalProperties: 'schema',
36
+ propertyNames: 'schema',
37
+ items: 'schema',
38
+ allOf: 'list',
39
+ if: 'schema',
40
+ then: 'schema'
41
+ };
42
+
43
+ const SUPPORTED_FORMATS = new Set(['date']);
44
+
45
+ // `type` is one name or a union of them: `["string", "null"]` is how a field that
46
+ // is a string or explicitly nothing — an edition that does not exist yet — says so.
47
+ // The string keywords apply to the string member; nothing constrains null.
48
+ const TYPES = new Set(['null', 'boolean', 'integer', 'number', 'string', 'array', 'object']);
49
+
50
+ const typeNames = (type) => (Array.isArray(type) ? type : [type]);
51
+
52
+ const typeOf = (value) => {
53
+ if (value === null) return 'null';
54
+ if (Array.isArray(value)) return 'array';
55
+ if (Number.isInteger(value)) return 'integer';
56
+ return typeof value;
57
+ };
58
+
59
+ const matchesType = (expected, actual) =>
60
+ typeNames(expected).some((name) => name === actual || (name === 'number' && actual === 'integer'));
61
+
62
+ const isCalendarDate = (value) => {
63
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
64
+ if (!match) return false;
65
+ const [, y, m, d] = match;
66
+ const date = new Date(Date.UTC(Number(y), Number(m) - 1, Number(d)));
67
+ return date.getUTCFullYear() === Number(y)
68
+ && date.getUTCMonth() === Number(m) - 1
69
+ && date.getUTCDate() === Number(d);
70
+ };
71
+
72
+ const resolveRef = (ref, root) => {
73
+ if (!ref.startsWith('#/')) throw new Error(`unsupported $ref "${ref}"`);
74
+ let node = root;
75
+ for (const segment of ref.slice(2).split('/')) {
76
+ node = Object.hasOwn(node ?? {}, segment) ? node[segment] : undefined;
77
+ if (node === undefined) throw new Error(`unresolvable $ref "${ref}"`);
78
+ }
79
+ return node;
80
+ };
81
+
82
+ // Throws on the first keyword or format this file does not enforce, anywhere in
83
+ // the schema — an unused $def and an absent property are walked like any other.
84
+ export function checkSchema(schema, path = '#') {
85
+ if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) {
86
+ throw new Error(`expected a schema object at ${path}`);
87
+ }
88
+ for (const keyword of Object.keys(schema)) {
89
+ if (!SUPPORTED.has(keyword)) throw new Error(`unsupported schema keyword "${keyword}" at ${path}`);
90
+ }
91
+ if (Object.hasOwn(schema, 'format') && !SUPPORTED_FORMATS.has(schema.format)) {
92
+ throw new Error(`unsupported format "${schema.format}" at ${path}`);
93
+ }
94
+ // A misspelled name inside a union would otherwise narrow the rule silently.
95
+ for (const name of Object.hasOwn(schema, 'type') ? typeNames(schema.type) : []) {
96
+ if (!TYPES.has(name)) throw new Error(`unsupported type "${name}" at ${path}`);
97
+ }
98
+ for (const [keyword, shape] of Object.entries(SUBSCHEMAS)) {
99
+ if (!Object.hasOwn(schema, keyword)) continue;
100
+ const value = schema[keyword];
101
+ if (shape === 'map') {
102
+ for (const name of Object.keys(value)) checkSchema(value[name], `${path}/${keyword}/${name}`);
103
+ } else if (shape === 'list') {
104
+ value.forEach((sub, index) => checkSchema(sub, `${path}/${keyword}/${index}`));
105
+ } else if (typeof value !== 'boolean') {
106
+ checkSchema(value, `${path}/${keyword}`);
107
+ }
108
+ }
109
+ return schema;
110
+ }
111
+
112
+ // A readable label for a conditional branch: { change: { const: 'changed' } }
113
+ // becomes `when change is "changed"`.
114
+ const conditionLabel = (condition) => {
115
+ const parts = Object.entries(condition.properties ?? {})
116
+ .filter(([, sub]) => Object.hasOwn(sub, 'const'))
117
+ .map(([name, sub]) => `${name} is ${JSON.stringify(sub.const)}`);
118
+ return parts.length ? ` (${parts.join(', ')})` : '';
119
+ };
120
+
121
+ export function validate(schema, data, { root = schema, path = '$', label = '' } = {}) {
122
+ for (const keyword of Object.keys(schema)) {
123
+ if (!SUPPORTED.has(keyword)) throw new Error(`unsupported schema keyword "${keyword}" at ${path}`);
124
+ }
125
+ if (schema.$ref) return validate(resolveRef(schema.$ref, root), data, { root, path, label });
126
+
127
+ const errors = [];
128
+ const fail = (message) => errors.push(`${path}${label}: ${message}`);
129
+ const actual = typeOf(data);
130
+
131
+ if (schema.type && !matchesType(schema.type, actual)) {
132
+ fail(`expected ${typeNames(schema.type).join(' or ')}, got ${actual}`);
133
+ return errors;
134
+ }
135
+ if (schema.enum && !schema.enum.includes(data)) {
136
+ fail(`${JSON.stringify(data)} is not one of ${schema.enum.map((v) => JSON.stringify(v)).join(', ')}`);
137
+ }
138
+ if (Object.hasOwn(schema, 'const') && data !== schema.const) {
139
+ fail(`expected ${JSON.stringify(schema.const)}`);
140
+ }
141
+
142
+ if (actual === 'string') {
143
+ if (schema.minLength !== undefined && data.length < schema.minLength) fail(`must not be empty`);
144
+ if (schema.pattern && !new RegExp(schema.pattern).test(data)) {
145
+ fail(`${JSON.stringify(data)} does not match ${schema.pattern}`);
146
+ }
147
+ if (Object.hasOwn(schema, 'format')) {
148
+ if (!SUPPORTED_FORMATS.has(schema.format)) throw new Error(`unsupported format "${schema.format}" at ${path}`);
149
+ if (!isCalendarDate(data)) fail(`${JSON.stringify(data)} is not a YYYY-MM-DD date`);
150
+ }
151
+ }
152
+
153
+ // Object.hasOwn throughout: an inherited `toString` or a JSON `__proto__` key is
154
+ // a property like any other and must meet the same rules.
155
+ if (actual === 'object') {
156
+ for (const name of schema.required ?? []) {
157
+ if (!Object.hasOwn(data, name)) fail(`missing required property "${name}"`);
158
+ }
159
+ for (const name of Object.keys(data)) {
160
+ const value = data[name];
161
+ const child = `${path}.${name}`;
162
+ if (schema.propertyNames && validate(schema.propertyNames, name, { root, path: child }).length) {
163
+ fail(`property name "${name}" does not match ${schema.propertyNames.pattern}`);
164
+ }
165
+ const sub = schema.properties && Object.hasOwn(schema.properties, name)
166
+ ? schema.properties[name]
167
+ : schema.additionalProperties;
168
+ if (sub === undefined || sub === true) continue;
169
+ if (sub === false) {
170
+ fail(`unknown property "${name}"`);
171
+ continue;
172
+ }
173
+ errors.push(...validate(sub, value, { root, path: child }));
174
+ }
175
+ }
176
+
177
+ if (actual === 'array') {
178
+ if (schema.uniqueItems === true && data.some((item, index) => data.slice(0, index).some((previous) => isDeepStrictEqual(item, previous)))) {
179
+ fail('items must be unique');
180
+ }
181
+ if (schema.minItems !== undefined && data.length < schema.minItems) {
182
+ fail(`needs at least ${schema.minItems} item(s)`);
183
+ }
184
+ if (schema.items) {
185
+ data.forEach((item, index) => {
186
+ errors.push(...validate(schema.items, item, { root, path: `${path}[${index}]` }));
187
+ });
188
+ }
189
+ }
190
+
191
+ for (const branch of schema.allOf ?? []) {
192
+ errors.push(...validate(branch, data, { root, path, label }));
193
+ }
194
+ if (schema.if) {
195
+ const holds = validate(schema.if, data, { root, path }).length === 0;
196
+ if (holds && schema.then) {
197
+ errors.push(...validate(schema.then, data, { root, path, label: conditionLabel(schema.if) }));
198
+ }
199
+ }
200
+
201
+ return errors;
202
+ }
@@ -0,0 +1,325 @@
1
+ import { dependencyErrors } from './dependencies.mjs';
2
+ export { validateDependencyGraph } from './dependencies.mjs';
3
+ import {checkSchema, validate} from './json-schema.mjs';
4
+
5
+ export const SETUP_SCHEMA_ID = 'urn:rer:setup:1';
6
+
7
+ const PUBLIC_KINDS = new Set(['text', 'email', 'url', 'select', 'colour', 'timezone']);
8
+
9
+ /** Match a validated condition against raw public input; whitespace is a value. */
10
+ export function setupConditionMatches(condition, values) {
11
+ const value = values[condition.field];
12
+ return condition.nonEmpty === true
13
+ ? typeof value === 'string' && value.length > 0
14
+ : value === condition.equals;
15
+ }
16
+
17
+ /** Install-time value validation shared by manifest defaults and consumers. */
18
+ export function validateSetupValue(field, value) {
19
+ if (typeof value !== 'string' || value.length > 16384) throw Error(`Invalid setup value for ${field.name}`);
20
+ if (!value) return;
21
+ if (field.kind === 'email') {
22
+ const email = value.trim().toLowerCase();
23
+ if (email.length > 254 || !/^[^\s@<>\x00-\x1f]+@[^\s@<>\x00-\x1f]+\.[^\s@<>\x00-\x1f]+$/.test(email)) throw Error(`Invalid email for ${field.name}`);
24
+ }
25
+ if (field.kind === 'url') {
26
+ try {
27
+ const url = new URL(value);
28
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.href.includes('?') || url.href.includes('#')) throw Error();
29
+ } catch { throw Error(`Invalid URL for ${field.name}`); }
30
+ }
31
+ if (field.kind === 'colour' && !/^#[a-f\d]{6}$/i.test(value)) throw Error(`Invalid colour for ${field.name}`);
32
+ if (field.kind === 'timezone') {
33
+ try { new Intl.DateTimeFormat('en', {timeZone: value}); }
34
+ catch { throw Error(`Invalid timezone for ${field.name}`); }
35
+ }
36
+ if (field.kind === 'select' && !field.options?.some(option => option.value === value)) throw Error(`Invalid selection for ${field.name}`);
37
+ }
38
+
39
+ /** Parse committed manifest bytes strictly, including duplicate object keys. */
40
+ export function parseSetupManifestBytes(bytes) {
41
+ if (!(bytes instanceof Uint8Array)) throw new Error('Setup manifest input must be bytes');
42
+ let text;
43
+ try {
44
+ text = new TextDecoder('utf-8', {fatal: true}).decode(bytes);
45
+ } catch {
46
+ throw new Error('Setup manifest must be valid UTF-8');
47
+ }
48
+ let value;
49
+ try {
50
+ value = JSON.parse(text);
51
+ } catch {
52
+ throw new Error('Setup manifest must contain valid JSON');
53
+ }
54
+ rejectDuplicateObjectKeys(text);
55
+ return value;
56
+ }
57
+
58
+ function rejectDuplicateObjectKeys(text) {
59
+ let position = 0;
60
+ const whitespace = () => { while (/\s/.test(text[position] ?? '') && position < text.length) position += 1; };
61
+ function stringToken() {
62
+ const start = position++;
63
+ while (position < text.length) {
64
+ const character = text[position++];
65
+ if (character === '\\') position += 1;
66
+ else if (character === '"') return JSON.parse(text.slice(start, position));
67
+ }
68
+ throw new Error('Setup manifest contains an incomplete JSON string');
69
+ }
70
+ function visit() {
71
+ whitespace();
72
+ const opening = text[position];
73
+ if (opening === '"') { stringToken(); return; }
74
+ if (opening !== '{' && opening !== '[') {
75
+ while (position < text.length && !/[\s,}\]]/.test(text[position])) position += 1;
76
+ return;
77
+ }
78
+ position += 1;
79
+ whitespace();
80
+ const closing = opening === '{' ? '}' : ']';
81
+ const names = new Set();
82
+ if (text[position] === closing) { position += 1; return; }
83
+ for (;;) {
84
+ if (opening === '{') {
85
+ whitespace();
86
+ const name = stringToken();
87
+ if (names.has(name)) throw new Error('Setup manifest contains a duplicate JSON field');
88
+ names.add(name);
89
+ whitespace();
90
+ position += 1; // JSON.parse above has already checked the colon.
91
+ }
92
+ visit();
93
+ whitespace();
94
+ const separator = text[position++];
95
+ if (separator === closing) return;
96
+ }
97
+ }
98
+ visit();
99
+ }
100
+
101
+ /** Semantic checks that cannot be expressed in the portable JSON Schema subset. */
102
+ export function setupManifestSemanticErrors(data) {
103
+ if (!data || typeof data !== 'object' || Array.isArray(data)
104
+ || !Array.isArray(data.fields) || !Array.isArray(data.groups) || !Array.isArray(data.steps)) return [];
105
+ const errors = dependencyErrors(data);
106
+ const fail = (path, message) => errors.push(`${path}: ${message}`);
107
+ const groups = new Map();
108
+ for (const [index, group] of data.groups.entries()) {
109
+ if (!group || typeof group !== 'object' || typeof group.id !== 'string') continue;
110
+ if (groups.has(group.id)) fail(`$.groups[${index}].id`, 'group IDs must be unique');
111
+ else groups.set(group.id, group);
112
+ }
113
+
114
+ const fields = new Map();
115
+ const fieldIndexes = new Map();
116
+ for (const [index, field] of data.fields.entries()) {
117
+ if (!field || typeof field !== 'object' || typeof field.name !== 'string') continue;
118
+ if (fields.has(field.name)) fail(`$.fields[${index}].name`, 'field names must be unique');
119
+ else {
120
+ fields.set(field.name, field);
121
+ fieldIndexes.set(field.name, index);
122
+ }
123
+ }
124
+
125
+ for (const [index, field] of data.fields.entries()) {
126
+ if (!field || typeof field !== 'object' || typeof field.name !== 'string') continue;
127
+ const path = `$.fields[${index}]`;
128
+ const isSecret = field.kind === 'secret';
129
+ if (!isSecret && !PUBLIC_KINDS.has(field.kind)) continue;
130
+ if (field.order !== undefined && field.order < 0) fail(`${path}.order`, 'field order must be zero or greater');
131
+ if (field.group !== undefined && !groups.has(field.group)) fail(`${path}.group`, 'group must reference a declared group');
132
+
133
+ if (isSecret) {
134
+ for (const name of ['default', 'prefill', 'options', 'runtimeEditable']) {
135
+ if (Object.hasOwn(field, name)) fail(`${path}.${name}`, 'secret fields cannot declare public input metadata');
136
+ }
137
+ if (field.input === 'entered') {
138
+ if (field.ownerVisible !== true) fail(`${path}.ownerVisible`, 'entered secrets must be owner-visible');
139
+ if (field.generate !== undefined) fail(`${path}.generate`, 'entered secrets cannot declare generation');
140
+ } else if (field.input === 'managed' && field.ownerVisible !== false) {
141
+ fail(`${path}.ownerVisible`, 'managed secrets must be hidden from the owner');
142
+ }
143
+ if (field.ownerVisible === true && field.generate === undefined && field.input !== 'entered') {
144
+ fail(`${path}.ownerVisible`, 'only generated or explicitly entered secret values may be shown to the owner');
145
+ }
146
+ } else {
147
+ for (const name of ['generate', 'ownerVisible', 'input']) {
148
+ if (Object.hasOwn(field, name)) fail(`${path}.${name}`, 'public fields cannot declare secret metadata');
149
+ }
150
+ if (Object.hasOwn(field, 'default')) {
151
+ try { validateSetupValue(field, field.default); }
152
+ catch (error) { fail(`${path}.default`, error.message); }
153
+ }
154
+ if (field.prefill !== undefined && field.kind !== 'email') fail(`${path}.prefill`, 'account-email prefill requires an email field');
155
+ if (field.kind === 'select') {
156
+ const options = Array.isArray(field.options) ? field.options : [];
157
+ const values = new Set();
158
+ for (const option of options) {
159
+ if (!option || typeof option.value !== 'string') continue;
160
+ if (values.has(option.value)) fail(`${path}.options`, 'select option values must be unique');
161
+ values.add(option.value);
162
+ }
163
+ if (Object.hasOwn(field, 'default') && !values.has(field.default)) {
164
+ fail(`${path}.default`, 'select default must match a declared option value');
165
+ }
166
+ } else if (Object.hasOwn(field, 'options')) {
167
+ fail(`${path}.options`, 'only select fields may declare options');
168
+ }
169
+ }
170
+
171
+ if (Object.hasOwn(field, 'localDefault')) validateLocalDefault(field, `${path}.localDefault`);
172
+
173
+ if (field.when !== undefined && field.when && typeof field.when === 'object') {
174
+ validateCondition(field.when, `${path}.when`, index, true);
175
+ }
176
+ validatePresentations(field.presentations, `${path}.presentations`, index, true);
177
+ }
178
+
179
+ for (const [index, group] of data.groups.entries()) {
180
+ if (!group || typeof group !== 'object') continue;
181
+ validatePresentations(group.presentations, `$.groups[${index}].presentations`, data.fields.length, false);
182
+ }
183
+
184
+ for (const [index, name] of (Array.isArray(data.runtime?.exclude) ? data.runtime.exclude : []).entries()) {
185
+ if (!fields.has(name)) fail(`$.runtime.exclude[${index}]`, 'runtime exclusion must reference a declared field');
186
+ }
187
+
188
+ const claims = data.steps.filter(step => step?.type === 'owner-claim');
189
+ // Binding-free editions may perform only extension steps. An owner input
190
+ // still requires its claim, and multiple claims remain ambiguous.
191
+ const ownerInput = data.fields.some(field => field?.prefill === 'account-email');
192
+ if (claims.length > 1 || (ownerInput && claims.length !== 1)) fail('$.steps', 'exactly one owner-claim step is required');
193
+ for (const [index, step] of data.steps.entries()) {
194
+ if (!step || typeof step !== 'object') continue;
195
+ const path = `$.steps[${index}]`;
196
+ if (step.type === 'extension') {
197
+ if (Object.keys(step).some(key => !['type', 'id'].includes(key))) fail(path, 'extension steps accept only type and id');
198
+ continue;
199
+ }
200
+ if (step.type !== 'owner-claim') continue;
201
+ const allowed = step.mode === 'idp' ? ['type','mode','emailField','dependency','path'] : step.mode === 'signed'
202
+ ? ['type', 'mode', 'emailField', 'tokenField', 'path']
203
+ : ['type', 'mode', 'emailField', 'tokenField'];
204
+ if (Object.keys(step).some(key => !allowed.includes(key))) fail(path, 'owner-claim step has fields that do not apply to its mode');
205
+ const email = fields.get(step.emailField);
206
+ if (!email || email.kind !== 'email' || email.prefill !== 'account-email') {
207
+ fail(`${path}.emailField`, 'owner-claim must reference a prefilled public email field');
208
+ }
209
+ if (step.mode === 'idp') {
210
+ if (!(data.dependencies??[]).some(d=>d.interface===step.dependency) || step.dependency!=='oidc-provider@1') fail(path, 'idp claim requires the declared identity dependency');
211
+ if (step.path!=='/auth/owner-claim') fail(path, 'idp claim requires the owner claim path');
212
+ } else if (step.mode === 'signed') {
213
+ const token = fields.get(step.tokenField);
214
+ if (!token || token.kind !== 'secret' || token.required !== true
215
+ || !['hex', 'base64'].includes(token.generate) || token.ownerVisible !== false) {
216
+ fail(`${path}.tokenField`, 'signed owner-claim must reference a required generated hidden secret');
217
+ }
218
+ if (typeof step.path !== 'string' || !safeSameAppPath(step.path)) {
219
+ fail(`${path}.path`, 'signed owner-claim path must be a safe same-app absolute path');
220
+ }
221
+ } else if (step.mode === 'setup-token') {
222
+ if (Object.hasOwn(step, 'path')) fail(`${path}.path`, 'legacy setup-token step does not declare a signed claim path');
223
+ if (step.tokenField !== undefined) {
224
+ const token = fields.get(step.tokenField);
225
+ if (!token || token.kind !== 'secret' || token.required !== true
226
+ || !['hex', 'base64'].includes(token.generate) || token.ownerVisible !== true) {
227
+ fail(`${path}.tokenField`, 'legacy setup-token must reference a required generated owner-visible secret');
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ // A value only the zero-setup local launch fills in: a literal for a public
234
+ // field, a generated value for an entered secret, or the chosen local origin.
235
+ function validateLocalDefault(field, path) {
236
+ const value = field.localDefault;
237
+ if (typeof value === 'string') {
238
+ if (field.kind === 'secret') fail(path, 'a secret takes a generated local default, never a literal');
239
+ else if (!value) fail(path, 'a literal local default must not be empty');
240
+ else { try { validateSetupValue(field, value); } catch (error) { fail(path, error.message); } }
241
+ return;
242
+ }
243
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return;
244
+ if (Object.hasOwn(value, 'generate') === Object.hasOwn(value, 'from')) {
245
+ fail(path, 'local default must declare exactly one of generate or from');
246
+ } else if (Object.hasOwn(value, 'generate')) {
247
+ if (field.kind !== 'secret' || field.input !== 'entered') fail(path, 'a generated local default requires an entered secret');
248
+ } else if (!['url', 'text'].includes(field.kind)) {
249
+ fail(path, 'the local origin default requires a url or text field');
250
+ }
251
+ }
252
+
253
+ function validateCondition(condition, path, beforeIndex, requireEarlier) {
254
+ if (Object.hasOwn(condition, 'equals') === Object.hasOwn(condition, 'nonEmpty')) {
255
+ fail(path, 'condition must declare exactly one of equals or nonEmpty');
256
+ return;
257
+ }
258
+ const dependency = fields.get(condition.field);
259
+ const dependencyIndex = fieldIndexes.get(condition.field);
260
+ if (!dependency) {
261
+ fail(`${path}.field`, 'condition must reference a declared field');
262
+ return;
263
+ }
264
+ if (requireEarlier && dependencyIndex >= beforeIndex) {
265
+ fail(`${path}.field`, 'condition must reference an earlier field');
266
+ return;
267
+ }
268
+ if (condition.nonEmpty === true ? !PUBLIC_KINDS.has(dependency.kind) : dependency.kind !== 'text' && dependency.kind !== 'select') {
269
+ fail(`${path}.field`, 'nonEmpty conditions require a public field; equality requires public text or select');
270
+ return;
271
+ }
272
+ if (condition.nonEmpty !== true && dependency.kind === 'select' && Array.isArray(dependency.options)
273
+ && !dependency.options.some(option => option?.value === condition.equals)) {
274
+ fail(`${path}.equals`, 'condition value must match a declared select option');
275
+ }
276
+ }
277
+
278
+ function validatePresentations(presentations, path, beforeIndex, requireEarlier) {
279
+ if (!Array.isArray(presentations)) return;
280
+ let discriminator;
281
+ const conditions = [];
282
+ for (const [variantIndex, presentation] of presentations.entries()) {
283
+ if (!presentation || typeof presentation !== 'object' || !presentation.when) continue;
284
+ const conditionPath = `${path}[${variantIndex}].when`;
285
+ validateCondition(presentation.when, conditionPath, beforeIndex, requireEarlier);
286
+ if (discriminator === undefined) discriminator = presentation.when.field;
287
+ else if (discriminator !== presentation.when.field) {
288
+ fail(`${path}[${variantIndex}].when.field`, 'presentation variants must share one condition field');
289
+ }
290
+ const condition = presentation.when;
291
+ const overlap = conditions.find(previous => {
292
+ if (previous.field !== condition.field) return false;
293
+ if (previous.nonEmpty === true && condition.nonEmpty === true) return true;
294
+ if (previous.nonEmpty === true) return typeof condition.equals === 'string' && condition.equals.length > 0;
295
+ if (condition.nonEmpty === true) return typeof previous.equals === 'string' && previous.equals.length > 0;
296
+ return previous.equals === condition.equals;
297
+ });
298
+ if (overlap) {
299
+ fail(conditionPath, overlap.nonEmpty !== true && condition.nonEmpty !== true
300
+ ? 'presentation condition values must be unique'
301
+ : 'presentation conditions must not overlap');
302
+ }
303
+ conditions.push(condition);
304
+ if (presentation.order !== undefined && presentation.order < 0) {
305
+ fail(`${path}[${variantIndex}].order`, 'presentation order must be zero or greater');
306
+ }
307
+ }
308
+ }
309
+ return errors;
310
+ }
311
+
312
+ function safeSameAppPath(path) {
313
+ if (!path.startsWith('/') || path.startsWith('//') || path.includes('\\') || /[?#]/.test(path)) return false;
314
+ const parts = path.split('/');
315
+ return parts.slice(1).every(part => part !== '.' && part !== '..' && (part.length > 0 || parts.length === 2));
316
+ }
317
+
318
+ /** Structural and cross-field validation used by the CLI and portable producer. */
319
+ export function validateSetupManifest(schema, data) {
320
+ if (schema?.$id !== SETUP_SCHEMA_ID) throw new Error('Expected setup schema version 1');
321
+ checkSchema(schema);
322
+ const errors = validate(schema, data);
323
+ if (errors.length) return errors;
324
+ return setupManifestSemanticErrors(data);
325
+ }
@@ -0,0 +1,21 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { SETUP_VALUE_LIMIT } from './input-limits.mjs';
3
+ export const credentialFingerprint=value=>createHash('sha256').update(value).digest('hex');
4
+ // Credentials remain in memory and are invalidated when their declared identity scope changes.
5
+ export function createSecretVault({fields,secretScopes={}},onSecrets=()=>{}){
6
+ let values={},scopes={};const invalidated=new Map();
7
+ const readiness=draft=>Object.fromEntries(Object.keys(secretScopes).map(key=>[key,Boolean(values[key]&&secretScopes[key].every(field=>scopes[key]?.[field]===draft[field]))]));
8
+ return {
9
+ invalidated(){return [...invalidated.values()];},
10
+ readiness(draft){return {...readiness(draft),scopes:structuredClone(scopes)};},
11
+ set(input,draft){
12
+ if(!input||typeof input!=='object'||Array.isArray(input))throw Error('Provide a JSON object through secure stdin.');
13
+ for(const [key,value] of Object.entries(input))if(!Object.hasOwn(secretScopes,key)||typeof value!=='string'||value.length>SETUP_VALUE_LIMIT||!fields[key]?.valid(value))throw Error('Secure input contains an unsupported or invalid credential.');
14
+ onSecrets(input);
15
+ for(const [key,value] of Object.entries(input)){values[key]=value;scopes[key]=Object.fromEntries(secretScopes[key].map(field=>[field,draft[field]]));}
16
+ },
17
+ invalidate(draft){for(const key of Object.keys(values))if(!secretScopes[key].every(field=>scopes[key]?.[field]===draft[field])){const fingerprint=credentialFingerprint(values[key]);invalidated.set(key+':'+fingerprint,{name:key,fingerprint});delete values[key];delete scopes[key];}},
18
+ merge(input,draft){const effective={...draft,...input};this.invalidate(effective);const ready=readiness(effective),merged={...input};for(const key of Object.keys(secretScopes))if(!merged[key]&&ready[key])merged[key]=values[key];return merged;},
19
+ clear(){values={};scopes={};}
20
+ };
21
+ }