@greatwait/contracts 0.1.0-alpha.3 → 0.1.0-alpha.4

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 (299) hide show
  1. package/README.md +11 -9
  2. package/dist/authority.d.ts +33 -27
  3. package/dist/authority.js +12 -2
  4. package/dist/billing.d.ts +1001 -0
  5. package/dist/billing.js +684 -0
  6. package/dist/callback-safety.d.ts +2 -2
  7. package/dist/callback-safety.js +2 -2
  8. package/dist/compatibility.d.ts +3 -3
  9. package/dist/compatibility.js +1 -1
  10. package/dist/data-export.d.ts +67 -0
  11. package/dist/data-export.js +182 -0
  12. package/dist/endpoints.d.ts +181 -240
  13. package/dist/endpoints.js +77 -81
  14. package/dist/events.d.ts +17 -17
  15. package/dist/events.js +1 -1
  16. package/dist/first-value-journey.d.ts +49 -55
  17. package/dist/first-value-journey.js +6 -13
  18. package/dist/forecast.d.ts +71 -71
  19. package/dist/forecast.js +1 -1
  20. package/dist/identifiers.d.ts +20 -20
  21. package/dist/identifiers.js +1 -1
  22. package/dist/identity.d.ts +41 -37
  23. package/dist/identity.js +5 -4
  24. package/dist/index.d.ts +10 -5
  25. package/dist/index.js +10 -5
  26. package/dist/mcp.d.ts +21 -19
  27. package/dist/mcp.js +1 -1
  28. package/dist/naming.d.ts +45 -0
  29. package/dist/naming.js +159 -0
  30. package/dist/oauth.d.ts +186 -172
  31. package/dist/oauth.js +1 -1
  32. package/dist/operation-definitions.d.ts +9 -0
  33. package/dist/operation-definitions.js +7 -0
  34. package/dist/operations.d.ts +4162 -2171
  35. package/dist/operations.js +2679 -908
  36. package/dist/organization-lifecycle.d.ts +34 -0
  37. package/dist/organization-lifecycle.js +122 -0
  38. package/dist/partition.d.ts +26 -0
  39. package/dist/policy.d.ts +4 -4
  40. package/dist/policy.js +9 -5
  41. package/dist/problems.d.ts +32 -26
  42. package/dist/problems.js +6 -2
  43. package/dist/protocol.d.ts +204 -75
  44. package/dist/protocol.js +108 -36
  45. package/dist/scheduling-records.d.ts +343 -285
  46. package/dist/scheduling-records.js +1 -1
  47. package/dist/scheduling.d.ts +670 -360
  48. package/dist/scheduling.js +382 -56
  49. package/dist/secrets.d.ts +119 -105
  50. package/dist/secrets.js +1 -1
  51. package/dist/signatures.d.ts +74 -74
  52. package/dist/signatures.js +1 -1
  53. package/dist/tenancy.d.ts +142 -68
  54. package/dist/tenancy.js +70 -17
  55. package/dist/test-sink.d.ts +127 -127
  56. package/dist/test-sink.js +1 -1
  57. package/dist/time.d.ts +4 -4
  58. package/dist/time.js +1 -1
  59. package/dist/timing.d.ts +65 -0
  60. package/dist/timing.js +82 -0
  61. package/dist/usage.d.ts +103 -47
  62. package/dist/usage.js +197 -31
  63. package/dist/workspace.d.ts +34 -34
  64. package/dist/workspace.js +1 -1
  65. package/dist/zod-values.d.ts +3 -0
  66. package/dist/zod-values.js +2 -0
  67. package/dist/zod.d.ts +2 -0
  68. package/dist/zod.js +2 -0
  69. package/generated/v1/examples/attempts.list.response.json +4 -0
  70. package/generated/v1/examples/attempts.read.response.json +4 -0
  71. package/generated/v1/examples/billing.autotopup.request.json +5 -0
  72. package/generated/v1/examples/billing.autotopup.response.json +120 -0
  73. package/generated/v1/examples/billing.autotopuppreview.request.json +19 -0
  74. package/generated/v1/examples/billing.autotopuppreview.response.json +45 -0
  75. package/generated/v1/examples/billing.budgets.request.json +13 -0
  76. package/generated/v1/examples/billing.budgets.response.json +22 -0
  77. package/generated/v1/examples/billing.ceiling.request.json +8 -0
  78. package/generated/v1/examples/billing.ceiling.response.json +120 -0
  79. package/generated/v1/examples/billing.change.request.json +5 -0
  80. package/generated/v1/examples/billing.change.response.json +12 -0
  81. package/generated/v1/examples/billing.funding.response.json +120 -0
  82. package/generated/v1/examples/billing.fundingquote.request.json +7 -0
  83. package/generated/v1/examples/billing.fundingquote.response.json +13 -0
  84. package/generated/v1/examples/billing.ledger.response.json +23 -0
  85. package/generated/v1/examples/billing.notices.response.json +41 -0
  86. package/generated/v1/examples/billing.notificationsettings.response.json +17 -0
  87. package/generated/v1/examples/billing.notificationsettingsupdate.request.json +9 -0
  88. package/generated/v1/examples/billing.notificationsettingsupdate.response.json +17 -0
  89. package/generated/v1/examples/billing.portal.response.json +3 -0
  90. package/generated/v1/examples/billing.projects.response.json +12 -0
  91. package/generated/v1/examples/billing.purchase.request.json +5 -0
  92. package/generated/v1/examples/billing.purchase.response.json +120 -0
  93. package/generated/v1/examples/billing.quote.request.json +4 -0
  94. package/generated/v1/examples/billing.quote.response.json +19 -0
  95. package/generated/v1/examples/billing.reconcile.response.json +73 -0
  96. package/generated/v1/examples/billing.refundrequest.request.json +10 -0
  97. package/generated/v1/examples/billing.refundrequest.response.json +11 -0
  98. package/generated/v1/examples/billing.refundresolution.request.json +4 -0
  99. package/generated/v1/examples/billing.refundresolution.response.json +120 -0
  100. package/generated/v1/examples/billing.summary.response.json +73 -0
  101. package/generated/v1/examples/endpoints.acknowledgeSigningKeyRotation.response.json +18 -0
  102. package/generated/v1/examples/endpoints.create.response.json +1 -1
  103. package/generated/v1/examples/endpoints.disable.response.json +0 -1
  104. package/generated/v1/examples/{endpoints.verify.response.json → endpoints.enable.response.json} +4 -5
  105. package/generated/v1/examples/endpoints.list.response.json +1 -2
  106. package/generated/v1/examples/endpoints.read.response.json +2 -2
  107. package/generated/v1/examples/endpoints.revealSigningSecret.response.json +7 -0
  108. package/generated/v1/examples/endpoints.rotateSigningKey.response.json +1 -2
  109. package/generated/v1/examples/endpoints.update.response.json +1 -1
  110. package/generated/v1/examples/executions.list.response.json +20 -0
  111. package/generated/v1/examples/exports.cancel.response.json +11 -0
  112. package/generated/v1/examples/exports.create.request.json +1 -0
  113. package/generated/v1/examples/exports.create.response.json +8 -0
  114. package/generated/v1/examples/exports.part.response.json +5 -0
  115. package/generated/v1/examples/exports.read.response.json +8 -0
  116. package/generated/v1/examples/occurrences.read.response.json +4 -0
  117. package/generated/v1/examples/organizations.close.request.json +5 -0
  118. package/generated/v1/examples/organizations.close.response.json +10 -0
  119. package/generated/v1/examples/organizations.lifecycle.response.json +10 -0
  120. package/generated/v1/examples/organizations.restore.request.json +3 -0
  121. package/generated/v1/examples/organizations.restore.response.json +10 -0
  122. package/generated/v1/examples/projects.archive.request.json +3 -0
  123. package/generated/v1/examples/projects.archive.response.json +14 -0
  124. package/generated/v1/examples/projects.create.response.json +10 -1
  125. package/generated/v1/examples/projects.delete.response.json +9 -0
  126. package/generated/v1/examples/projects.list.response.json +12 -2
  127. package/generated/v1/examples/projects.rename.request.json +3 -0
  128. package/generated/v1/examples/projects.rename.response.json +14 -0
  129. package/generated/v1/examples/schedules.archive.response.json +3 -0
  130. package/generated/v1/examples/schedules.cancel.response.json +3 -0
  131. package/generated/v1/examples/schedules.create.request.json +4 -0
  132. package/generated/v1/examples/schedules.create.response.json +3 -0
  133. package/generated/v1/examples/schedules.list.response.json +3 -0
  134. package/generated/v1/examples/schedules.pause.response.json +3 -0
  135. package/generated/v1/examples/schedules.read.response.json +6 -0
  136. package/generated/v1/examples/schedules.replace.request.json +4 -0
  137. package/generated/v1/examples/schedules.replace.response.json +3 -0
  138. package/generated/v1/examples/schedules.resume.response.json +3 -0
  139. package/generated/v1/examples/timing.summary.response.json +35 -0
  140. package/generated/v1/examples/usage.read.response.json +1 -0
  141. package/generated/v1/first-value-journey.json +19 -10
  142. package/generated/v1/manifest.json +936 -23
  143. package/generated/v1/openapi.json +29983 -5784
  144. package/generated/v1/policy-matrix.json +278 -47
  145. package/generated/v1/schemas/attempts.list.response.json +24 -1
  146. package/generated/v1/schemas/attempts.read.response.json +24 -1
  147. package/generated/v1/schemas/billing.autotopup.request-headers.json +13 -0
  148. package/generated/v1/schemas/billing.autotopup.request.json +22 -0
  149. package/generated/v1/schemas/billing.autotopup.response-headers.json +10 -0
  150. package/generated/v1/schemas/billing.autotopup.response.json +552 -0
  151. package/generated/v1/schemas/billing.autotopuppreview.request.json +77 -0
  152. package/generated/v1/schemas/billing.autotopuppreview.response.json +277 -0
  153. package/generated/v1/schemas/billing.budgets.request-headers.json +13 -0
  154. package/generated/v1/schemas/billing.budgets.request.json +66 -0
  155. package/generated/v1/schemas/billing.budgets.response-headers.json +10 -0
  156. package/generated/v1/schemas/billing.budgets.response.json +92 -0
  157. package/generated/v1/schemas/billing.ceiling.request-headers.json +13 -0
  158. package/generated/v1/schemas/billing.ceiling.request.json +40 -0
  159. package/generated/v1/schemas/billing.ceiling.response-headers.json +10 -0
  160. package/generated/v1/schemas/billing.ceiling.response.json +552 -0
  161. package/generated/v1/schemas/billing.change.request-headers.json +13 -0
  162. package/generated/v1/schemas/billing.change.request.json +22 -0
  163. package/generated/v1/schemas/billing.change.response.json +119 -0
  164. package/generated/v1/schemas/billing.funding.response.json +552 -0
  165. package/generated/v1/schemas/billing.fundingquote.request.json +28 -0
  166. package/generated/v1/schemas/billing.fundingquote.response.json +61 -0
  167. package/generated/v1/schemas/billing.ledger.query.json +16 -0
  168. package/generated/v1/schemas/billing.ledger.response.json +125 -0
  169. package/generated/v1/schemas/billing.notices.query.json +16 -0
  170. package/generated/v1/schemas/billing.notices.response.json +278 -0
  171. package/generated/v1/schemas/billing.notificationsettings.response.json +70 -0
  172. package/generated/v1/schemas/billing.notificationsettingsupdate.request-headers.json +13 -0
  173. package/generated/v1/schemas/billing.notificationsettingsupdate.request.json +36 -0
  174. package/generated/v1/schemas/billing.notificationsettingsupdate.response-headers.json +10 -0
  175. package/generated/v1/schemas/billing.notificationsettingsupdate.response.json +70 -0
  176. package/generated/v1/schemas/billing.portal.response.json +11 -0
  177. package/generated/v1/schemas/billing.projects.response.json +119 -0
  178. package/generated/v1/schemas/billing.purchase.request-headers.json +13 -0
  179. package/generated/v1/schemas/billing.purchase.request.json +22 -0
  180. package/generated/v1/schemas/billing.purchase.response-headers.json +10 -0
  181. package/generated/v1/schemas/billing.purchase.response.json +552 -0
  182. package/generated/v1/schemas/billing.quote.request.json +20 -0
  183. package/generated/v1/schemas/billing.quote.response.json +86 -0
  184. package/generated/v1/schemas/billing.reconcile.response.json +468 -0
  185. package/generated/v1/schemas/billing.refundrequest.request-headers.json +13 -0
  186. package/generated/v1/schemas/billing.refundrequest.request.json +42 -0
  187. package/generated/v1/schemas/billing.refundrequest.response-headers.json +10 -0
  188. package/generated/v1/schemas/billing.refundrequest.response.json +45 -0
  189. package/generated/v1/schemas/billing.refundresolution.request-headers.json +13 -0
  190. package/generated/v1/schemas/billing.refundresolution.request.json +61 -0
  191. package/generated/v1/schemas/billing.refundresolution.response-headers.json +10 -0
  192. package/generated/v1/schemas/billing.refundresolution.response.json +552 -0
  193. package/generated/v1/schemas/billing.summary.response.json +468 -0
  194. package/generated/v1/schemas/context.read.response.json +2 -0
  195. package/generated/v1/schemas/credentials.connection.response.json +2 -0
  196. package/generated/v1/schemas/credentials.create.request.json +2 -0
  197. package/generated/v1/schemas/credentials.create.response.json +2 -0
  198. package/generated/v1/schemas/credentials.list.response.json +2 -0
  199. package/generated/v1/schemas/credentials.revoke.response.json +2 -0
  200. package/generated/v1/schemas/credentials.rotate.response.json +4 -0
  201. package/generated/v1/schemas/{endpoints.verify.request-headers.json → endpoints.acknowledgeSigningKeyRotation.request-headers.json} +2 -1
  202. package/generated/v1/schemas/{endpoints.verify.response.json → endpoints.acknowledgeSigningKeyRotation.response.json} +8 -21
  203. package/generated/v1/schemas/endpoints.create.request.json +5 -0
  204. package/generated/v1/schemas/endpoints.create.response.json +8 -21
  205. package/generated/v1/schemas/endpoints.disable.response.json +8 -21
  206. package/generated/v1/schemas/endpoints.enable.response-headers.json +11 -0
  207. package/generated/v1/schemas/endpoints.enable.response.json +87 -0
  208. package/generated/v1/schemas/endpoints.list.response.json +8 -21
  209. package/generated/v1/schemas/endpoints.read.response.json +8 -21
  210. package/generated/v1/schemas/endpoints.revealSigningSecret.response.json +43 -0
  211. package/generated/v1/schemas/endpoints.rotateSigningKey.response.json +8 -21
  212. package/generated/v1/schemas/endpoints.update.request.json +5 -0
  213. package/generated/v1/schemas/endpoints.update.response.json +8 -21
  214. package/generated/v1/schemas/executions.list.query.json +46 -0
  215. package/generated/v1/schemas/executions.list.response.json +116 -0
  216. package/generated/v1/schemas/exports.cancel.response.json +48 -0
  217. package/generated/v1/schemas/exports.create.request.json +6 -0
  218. package/generated/v1/schemas/exports.create.response.json +38 -0
  219. package/generated/v1/schemas/exports.part.query.json +11 -0
  220. package/generated/v1/schemas/exports.part.response.json +22 -0
  221. package/generated/v1/schemas/exports.read.response.json +38 -0
  222. package/generated/v1/schemas/grants.list.response.json +2 -0
  223. package/generated/v1/schemas/grants.revoke.response.json +2 -0
  224. package/generated/v1/schemas/occurrences.list.query.json +5 -0
  225. package/generated/v1/schemas/occurrences.list.response.json +23 -0
  226. package/generated/v1/schemas/occurrences.read.response.json +23 -0
  227. package/generated/v1/schemas/occurrences.redeliver.response.json +24 -1
  228. package/generated/v1/schemas/organizations.close.request.json +20 -0
  229. package/generated/v1/schemas/organizations.close.response.json +68 -0
  230. package/generated/v1/schemas/organizations.lifecycle.response.json +68 -0
  231. package/generated/v1/schemas/organizations.restore.request.json +13 -0
  232. package/generated/v1/schemas/organizations.restore.response.json +68 -0
  233. package/generated/v1/schemas/projects.archive.request-headers.json +11 -0
  234. package/generated/v1/schemas/projects.archive.request.json +10 -0
  235. package/generated/v1/schemas/projects.archive.response-headers.json +11 -0
  236. package/generated/v1/schemas/projects.archive.response.json +79 -0
  237. package/generated/v1/schemas/projects.create.response.json +55 -1
  238. package/generated/v1/schemas/projects.delete.request-headers.json +11 -0
  239. package/generated/v1/schemas/projects.delete.response.json +37 -0
  240. package/generated/v1/schemas/projects.list.response.json +61 -2
  241. package/generated/v1/schemas/projects.rename.request-headers.json +11 -0
  242. package/generated/v1/schemas/projects.rename.request.json +12 -0
  243. package/generated/v1/schemas/projects.rename.response-headers.json +11 -0
  244. package/generated/v1/schemas/projects.rename.response.json +79 -0
  245. package/generated/v1/schemas/schedules.archive.response.json +90 -2
  246. package/generated/v1/schemas/schedules.cancel.response.json +90 -2
  247. package/generated/v1/schemas/schedules.create.request.json +24 -3
  248. package/generated/v1/schemas/schedules.create.response.json +90 -2
  249. package/generated/v1/schemas/schedules.list.query.json +13 -0
  250. package/generated/v1/schemas/schedules.list.response.json +67 -2
  251. package/generated/v1/schemas/schedules.pause.response.json +90 -2
  252. package/generated/v1/schemas/schedules.read.response.json +90 -2
  253. package/generated/v1/schemas/schedules.replace.request.json +24 -3
  254. package/generated/v1/schemas/schedules.replace.response.json +90 -2
  255. package/generated/v1/schemas/schedules.resume.response.json +90 -2
  256. package/generated/v1/schemas/timing.summary.response.json +191 -0
  257. package/generated/v1/schemas/usage.read.query.json +7 -0
  258. package/generated/v1/schemas/usage.read.response.json +36 -1
  259. package/generated/v1/snippets/billing.autotopup.json +6 -0
  260. package/generated/v1/snippets/billing.autotopuppreview.json +6 -0
  261. package/generated/v1/snippets/billing.budgets.json +6 -0
  262. package/generated/v1/snippets/billing.ceiling.json +6 -0
  263. package/generated/v1/snippets/billing.change.json +6 -0
  264. package/generated/v1/snippets/billing.funding.json +6 -0
  265. package/generated/v1/snippets/billing.fundingquote.json +6 -0
  266. package/generated/v1/snippets/billing.ledger.json +6 -0
  267. package/generated/v1/snippets/billing.notices.json +6 -0
  268. package/generated/v1/snippets/billing.notificationsettings.json +6 -0
  269. package/generated/v1/snippets/billing.notificationsettingsupdate.json +6 -0
  270. package/generated/v1/snippets/billing.portal.json +6 -0
  271. package/generated/v1/snippets/billing.projects.json +6 -0
  272. package/generated/v1/snippets/billing.purchase.json +6 -0
  273. package/generated/v1/snippets/billing.quote.json +6 -0
  274. package/generated/v1/snippets/billing.reconcile.json +6 -0
  275. package/generated/v1/snippets/billing.refundrequest.json +6 -0
  276. package/generated/v1/snippets/billing.refundresolution.json +6 -0
  277. package/generated/v1/snippets/billing.summary.json +6 -0
  278. package/generated/v1/snippets/endpoints.acknowledgeSigningKeyRotation.json +6 -0
  279. package/generated/v1/snippets/{endpoints.verify.json → endpoints.enable.json} +3 -3
  280. package/generated/v1/snippets/endpoints.revealSigningSecret.json +6 -0
  281. package/generated/v1/snippets/executions.list.json +6 -0
  282. package/generated/v1/snippets/exports.cancel.json +6 -0
  283. package/generated/v1/snippets/exports.create.json +6 -0
  284. package/generated/v1/snippets/exports.part.json +6 -0
  285. package/generated/v1/snippets/exports.read.json +6 -0
  286. package/generated/v1/snippets/organizations.close.json +6 -0
  287. package/generated/v1/snippets/organizations.lifecycle.json +6 -0
  288. package/generated/v1/snippets/organizations.restore.json +6 -0
  289. package/generated/v1/snippets/projects.archive.json +6 -0
  290. package/generated/v1/snippets/projects.delete.json +6 -0
  291. package/generated/v1/snippets/projects.rename.json +6 -0
  292. package/generated/v1/snippets/schedules.create.json +3 -3
  293. package/generated/v1/snippets/schedules.replace.json +3 -3
  294. package/generated/v1/snippets/timing.summary.json +6 -0
  295. package/package.json +1 -6
  296. package/dist/callback-ownership.d.ts +0 -58
  297. package/dist/callback-ownership.js +0 -71
  298. package/generated/v1/callback-endpoint-verification.json +0 -59
  299. /package/generated/v1/schemas/{endpoints.verify.response-headers.json → endpoints.acknowledgeSigningKeyRotation.response-headers.json} +0 -0
package/dist/tenancy.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from "./zod.js";
2
2
  /**
3
3
  * Organizations, projects, and environments.
4
4
  *
@@ -22,48 +22,70 @@ import { z } from "zod";
22
22
  * our surface never depends on the identity vendor and a caller never learns a
23
23
  * second spelling of their own tenant. See `identity.ts`.
24
24
  */
25
- export declare const organizationSchema: z.ZodObject<{
26
- externalId: z.ZodString;
27
- id: z.ZodString;
28
- version: z.ZodNumber;
29
- }, z.core.$strip>;
25
+ export declare const organizationSchema: import("zod").ZodObject<{
26
+ externalId: import("zod").ZodString;
27
+ id: import("zod").ZodString;
28
+ version: import("zod").ZodNumber;
29
+ }, import("zod/v4/core").$strip>;
30
30
  export type Organization = z.infer<typeof organizationSchema>;
31
31
  /**
32
32
  * Whether a project accepts new work.
33
33
  *
34
- * `archived` is reversible and stops new schedules without destroying history;
35
- * `deleted` is a tombstone. Deletion keeps the row rather than removing it,
36
- * because a project identifier that vanishes makes every occurrence and audit
37
- * entry that referenced it unexplainable — and because reusing a deleted
38
- * project's identifier must be impossible.
34
+ * `archived` is reversible and stops new work without destroying history.
35
+ * `deleted` is terminal and exists only while permanent destruction is being
36
+ * completed; a successful delete removes the project and all of its rows.
39
37
  */
40
38
  export declare const PROJECT_STATUSES: readonly ["active", "archived", "deleted"];
41
- export declare const projectStatusSchema: z.ZodEnum<{
39
+ export declare const projectStatusSchema: import("zod").ZodEnum<{
42
40
  active: "active";
43
- archived: "archived";
44
41
  deleted: "deleted";
42
+ archived: "archived";
45
43
  }>;
46
44
  export type ProjectStatus = z.infer<typeof projectStatusSchema>;
47
45
  /**
48
- * A human label for a project. Mutable, non-unique, and never an identifier.
46
+ * A human label for a project. Mutable, unique within its organization by
47
+ * `projectNameKey`, and never an identifier.
49
48
  *
50
49
  * Deliberately not a slug. A name that doubles as an identifier invites callers
51
50
  * to construct URLs from it, and then renaming a project becomes a breaking
52
51
  * change for them. Identifiers are server-assigned and opaque; names are for
53
- * people.
52
+ * people — which is also why two projects may not share one (FXO-2256): a
53
+ * switcher with two "Billing reminders" in it is a switcher nobody can use.
54
+ */
55
+ export declare const projectNameSchema: import("zod").ZodString;
56
+ /**
57
+ * How two project names are compared for uniqueness (FXO-2256).
58
+ *
59
+ * Per organization, ignoring case and surrounding or repeated spaces:
60
+ * "Billing reminders" and "billing Reminders" are one name, which is the
61
+ * mistake somebody actually makes — retyping a name they already used.
62
+ *
63
+ * **Two statements of one rule, kept equal by construction.** The database
64
+ * computes this key itself, in SQL, on every insert and rename (the triggers
65
+ * in `apps/api/migrations/0014_project_name_uniqueness.sql`), and a unique
66
+ * index over it refuses a second active project with the same one — so no
67
+ * writer can bypass the rule by forgetting a column. This function is the
68
+ * form's and the store's copy, for a refusal before the request, and it is
69
+ * deliberately no cleverer than SQLite: case folds for ASCII letters only
70
+ * (`lower()` there knows no other alphabet), and the whitespace it collapses
71
+ * is spaces, tabs and newlines. Two names differing only in the case of an
72
+ * accented letter are two names — an edge stated rather than hidden, because
73
+ * a rule the form applied and the database did not would refuse in the form
74
+ * what the database admits. A parity case runs both over the same corpus.
54
75
  */
55
- export declare const projectNameSchema: z.ZodString;
56
- export declare const projectSchema: z.ZodObject<{
57
- id: z.ZodString;
58
- name: z.ZodString;
59
- organization: z.ZodString;
60
- status: z.ZodEnum<{
76
+ export declare function projectNameKey(name: string): string;
77
+ export declare const projectSchema: import("zod").ZodObject<{
78
+ createdAt: import("zod").ZodNullable<import("zod").ZodString>;
79
+ id: import("zod").ZodString;
80
+ name: import("zod").ZodString;
81
+ organization: import("zod").ZodString;
82
+ status: import("zod").ZodEnum<{
61
83
  active: "active";
62
- archived: "archived";
63
84
  deleted: "deleted";
85
+ archived: "archived";
64
86
  }>;
65
- version: z.ZodNumber;
66
- }, z.core.$strip>;
87
+ version: import("zod").ZodNumber;
88
+ }, import("zod/v4/core").$strip>;
67
89
  export type Project = z.infer<typeof projectSchema>;
68
90
  /**
69
91
  * One of a project's two environments.
@@ -73,14 +95,14 @@ export type Project = z.infer<typeof projectSchema>;
73
95
  * schedule, endpoint and credential inside it, all of which were created under
74
96
  * the assumption that nothing they did could reach a customer.
75
97
  */
76
- export declare const environmentSchema: z.ZodObject<{
77
- id: z.ZodString;
78
- kind: z.ZodEnum<{
98
+ export declare const environmentSchema: import("zod").ZodObject<{
99
+ id: import("zod").ZodString;
100
+ kind: import("zod").ZodEnum<{
79
101
  test: "test";
80
102
  live: "live";
81
103
  }>;
82
- project: z.ZodString;
83
- }, z.core.$strip>;
104
+ project: import("zod").ZodString;
105
+ }, import("zod/v4/core").$strip>;
84
106
  export type Environment = z.infer<typeof environmentSchema>;
85
107
  /**
86
108
  * A project together with both of its environments.
@@ -88,27 +110,28 @@ export type Environment = z.infer<typeof environmentSchema>;
88
110
  * Returned as a unit because a project without both is not a valid state: they
89
111
  * are provisioned in the same transaction, and any caller that has one has both.
90
112
  */
91
- export declare const provisionedProjectSchema: z.ZodObject<{
92
- environments: z.ZodArray<z.ZodObject<{
93
- id: z.ZodString;
94
- kind: z.ZodEnum<{
113
+ export declare const provisionedProjectSchema: import("zod").ZodObject<{
114
+ environments: import("zod").ZodArray<import("zod").ZodObject<{
115
+ id: import("zod").ZodString;
116
+ kind: import("zod").ZodEnum<{
95
117
  test: "test";
96
118
  live: "live";
97
119
  }>;
98
- project: z.ZodString;
99
- }, z.core.$strip>>;
100
- project: z.ZodObject<{
101
- id: z.ZodString;
102
- name: z.ZodString;
103
- organization: z.ZodString;
104
- status: z.ZodEnum<{
120
+ project: import("zod").ZodString;
121
+ }, import("zod/v4/core").$strip>>;
122
+ project: import("zod").ZodObject<{
123
+ createdAt: import("zod").ZodNullable<import("zod").ZodString>;
124
+ id: import("zod").ZodString;
125
+ name: import("zod").ZodString;
126
+ organization: import("zod").ZodString;
127
+ status: import("zod").ZodEnum<{
105
128
  active: "active";
106
- archived: "archived";
107
129
  deleted: "deleted";
130
+ archived: "archived";
108
131
  }>;
109
- version: z.ZodNumber;
110
- }, z.core.$strip>;
111
- }, z.core.$strip>;
132
+ version: import("zod").ZodNumber;
133
+ }, import("zod/v4/core").$strip>;
134
+ }, import("zod/v4/core").$strip>;
112
135
  export type ProvisionedProject = z.infer<typeof provisionedProjectSchema>;
113
136
  /**
114
137
  * What a signed-in member is told about a project they may act in.
@@ -120,23 +143,41 @@ export type ProvisionedProject = z.infer<typeof provisionedProjectSchema>;
120
143
  * `kind`. Publishing an identifier no operation accepts invites a client to
121
144
  * build on it and then constrains us to keep it.
122
145
  *
123
- * `{id, name}` is exactly what the OAuth consent prompt already hands a browser,
124
- * and the framing there applies here word for word: this is a display aid rather
125
- * than an authorization control. Whatever the list shows, the authority for a
126
- * request is re-resolved from the member's organization when the request
127
- * arrives.
146
+ * This remains a display aid rather than an authorization control. Whatever the
147
+ * list shows, the authority for a request is re-resolved from the member's
148
+ * organization when the request arrives. Lifecycle state and version make an
149
+ * archived row manageable without making it a valid work target.
128
150
  */
129
- export declare const projectSummarySchema: z.ZodObject<{
130
- environmentKinds: z.ZodArray<z.ZodEnum<{
151
+ export declare const projectSummarySchema: import("zod").ZodObject<{
152
+ createdAt: import("zod").ZodNullable<import("zod").ZodString>;
153
+ deletionCounts: import("zod").ZodObject<{
154
+ endpoints: import("zod").ZodNumber;
155
+ keys: import("zod").ZodNumber;
156
+ runs: import("zod").ZodNumber;
157
+ schedules: import("zod").ZodNumber;
158
+ }, import("zod/v4/core").$strip>;
159
+ environmentKinds: import("zod").ZodArray<import("zod").ZodEnum<{
131
160
  test: "test";
132
161
  live: "live";
133
162
  }>>;
134
- id: z.ZodString;
135
- name: z.ZodString;
136
- }, z.core.$strip>;
163
+ id: import("zod").ZodString;
164
+ name: import("zod").ZodString;
165
+ status: import("zod").ZodEnum<{
166
+ active: "active";
167
+ archived: "archived";
168
+ }>;
169
+ version: import("zod").ZodNumber;
170
+ }, import("zod/v4/core").$strip>;
137
171
  export type ProjectSummary = z.infer<typeof projectSummarySchema>;
172
+ export declare const projectDeletionCountsSchema: import("zod").ZodObject<{
173
+ endpoints: import("zod").ZodNumber;
174
+ keys: import("zod").ZodNumber;
175
+ runs: import("zod").ZodNumber;
176
+ schedules: import("zod").ZodNumber;
177
+ }, import("zod/v4/core").$strip>;
178
+ export type ProjectDeletionCounts = z.infer<typeof projectDeletionCountsSchema>;
138
179
  /**
139
- * Every project in the member's organization that they may act in.
180
+ * Every active or archived project in the member's organization.
140
181
  *
141
182
  * **Unpaged, with a stated ceiling, because the store beneath it is.**
142
183
  * `listProjects` reads an organization's projects in one go; declaring a page
@@ -147,16 +188,29 @@ export type ProjectSummary = z.infer<typeof projectSummarySchema>;
147
188
  * a truncated answer.
148
189
  */
149
190
  export declare const PROJECT_LIST_LIMIT = 200;
150
- export declare const projectListSchema: z.ZodObject<{
151
- data: z.ZodArray<z.ZodObject<{
152
- environmentKinds: z.ZodArray<z.ZodEnum<{
191
+ export declare const projectListSchema: import("zod").ZodObject<{
192
+ data: import("zod").ZodArray<import("zod").ZodObject<{
193
+ createdAt: import("zod").ZodNullable<import("zod").ZodString>;
194
+ deletionCounts: import("zod").ZodObject<{
195
+ endpoints: import("zod").ZodNumber;
196
+ keys: import("zod").ZodNumber;
197
+ runs: import("zod").ZodNumber;
198
+ schedules: import("zod").ZodNumber;
199
+ }, import("zod/v4/core").$strip>;
200
+ environmentKinds: import("zod").ZodArray<import("zod").ZodEnum<{
153
201
  test: "test";
154
202
  live: "live";
155
203
  }>>;
156
- id: z.ZodString;
157
- name: z.ZodString;
158
- }, z.core.$strip>>;
159
- }, z.core.$strip>;
204
+ id: import("zod").ZodString;
205
+ name: import("zod").ZodString;
206
+ status: import("zod").ZodEnum<{
207
+ active: "active";
208
+ archived: "archived";
209
+ }>;
210
+ version: import("zod").ZodNumber;
211
+ }, import("zod/v4/core").$strip>>;
212
+ total: import("zod").ZodNumber;
213
+ }, import("zod/v4/core").$strip>;
160
214
  export type ProjectList = z.infer<typeof projectListSchema>;
161
215
  /**
162
216
  * What a member sends to create a project.
@@ -168,11 +222,31 @@ export type ProjectList = z.infer<typeof projectListSchema>;
168
222
  * a project observable without one of them is a state this product does not
169
223
  * have.
170
224
  *
171
- * There is no `status`: a project is created `active`, and a caller that could
172
- * create an archived one would be creating something it must then unarchive
173
- * through an operation that does not exist.
225
+ * There is no `status`: a project is created `active`; archiving is a separate
226
+ * administrator operation.
174
227
  */
175
- export declare const projectCreateRequestSchema: z.ZodObject<{
176
- name: z.ZodString;
177
- }, z.core.$strip>;
228
+ export declare const projectCreateRequestSchema: import("zod").ZodObject<{
229
+ name: import("zod").ZodString;
230
+ }, import("zod/v4/core").$strip>;
178
231
  export type ProjectCreateRequest = z.infer<typeof projectCreateRequestSchema>;
232
+ /** Rename the project without changing its opaque identifier. */
233
+ export declare const projectUpdateRequestSchema: import("zod").ZodObject<{
234
+ name: import("zod").ZodString;
235
+ }, import("zod/v4/core").$strip>;
236
+ export type ProjectUpdateRequest = z.infer<typeof projectUpdateRequestSchema>;
237
+ /** Park or restore the complete project without rewriting its schedules. */
238
+ export declare const projectArchiveRequestSchema: import("zod").ZodObject<{
239
+ archived: import("zod").ZodBoolean;
240
+ }, import("zod/v4/core").$strip>;
241
+ export type ProjectArchiveRequest = z.infer<typeof projectArchiveRequestSchema>;
242
+ /** What a successful permanent deletion removed. */
243
+ export declare const projectDeletedSchema: import("zod").ZodObject<{
244
+ deleted: import("zod").ZodLiteral<true>;
245
+ destroyed: import("zod").ZodObject<{
246
+ endpoints: import("zod").ZodNumber;
247
+ keys: import("zod").ZodNumber;
248
+ runs: import("zod").ZodNumber;
249
+ schedules: import("zod").ZodNumber;
250
+ }, import("zod/v4/core").$strip>;
251
+ }, import("zod/v4/core").$strip>;
252
+ export type ProjectDeleted = z.infer<typeof projectDeletedSchema>;
package/dist/tenancy.js CHANGED
@@ -1,7 +1,8 @@
1
- import { z } from "zod";
1
+ import { z } from "./zod.js";
2
2
  import { environmentKindSchema } from "./authority.js";
3
3
  import { environmentIdSchema, organizationIdSchema, projectIdSchema, } from "./identifiers.js";
4
4
  import { versionSchema } from "./protocol.js";
5
+ import { timestampSchema } from "./time.js";
5
6
  /**
6
7
  * Organizations, projects, and environments.
7
8
  *
@@ -33,28 +34,57 @@ export const organizationSchema = z.object({
33
34
  /**
34
35
  * Whether a project accepts new work.
35
36
  *
36
- * `archived` is reversible and stops new schedules without destroying history;
37
- * `deleted` is a tombstone. Deletion keeps the row rather than removing it,
38
- * because a project identifier that vanishes makes every occurrence and audit
39
- * entry that referenced it unexplainable — and because reusing a deleted
40
- * project's identifier must be impossible.
37
+ * `archived` is reversible and stops new work without destroying history.
38
+ * `deleted` is terminal and exists only while permanent destruction is being
39
+ * completed; a successful delete removes the project and all of its rows.
41
40
  */
42
41
  export const PROJECT_STATUSES = ["active", "archived", "deleted"];
43
42
  export const projectStatusSchema = z.enum(PROJECT_STATUSES);
44
43
  /**
45
- * A human label for a project. Mutable, non-unique, and never an identifier.
44
+ * A human label for a project. Mutable, unique within its organization by
45
+ * `projectNameKey`, and never an identifier.
46
46
  *
47
47
  * Deliberately not a slug. A name that doubles as an identifier invites callers
48
48
  * to construct URLs from it, and then renaming a project becomes a breaking
49
49
  * change for them. Identifiers are server-assigned and opaque; names are for
50
- * people.
50
+ * people — which is also why two projects may not share one (FXO-2256): a
51
+ * switcher with two "Billing reminders" in it is a switcher nobody can use.
51
52
  */
52
53
  export const projectNameSchema = z
53
54
  .string()
54
55
  .trim()
55
56
  .min(1, "must not be blank")
56
57
  .max(120, "must be at most 120 characters");
58
+ /**
59
+ * How two project names are compared for uniqueness (FXO-2256).
60
+ *
61
+ * Per organization, ignoring case and surrounding or repeated spaces:
62
+ * "Billing reminders" and "billing Reminders" are one name, which is the
63
+ * mistake somebody actually makes — retyping a name they already used.
64
+ *
65
+ * **Two statements of one rule, kept equal by construction.** The database
66
+ * computes this key itself, in SQL, on every insert and rename (the triggers
67
+ * in `apps/api/migrations/0014_project_name_uniqueness.sql`), and a unique
68
+ * index over it refuses a second active project with the same one — so no
69
+ * writer can bypass the rule by forgetting a column. This function is the
70
+ * form's and the store's copy, for a refusal before the request, and it is
71
+ * deliberately no cleverer than SQLite: case folds for ASCII letters only
72
+ * (`lower()` there knows no other alphabet), and the whitespace it collapses
73
+ * is spaces, tabs and newlines. Two names differing only in the case of an
74
+ * accented letter are two names — an edge stated rather than hidden, because
75
+ * a rule the form applied and the database did not would refuse in the form
76
+ * what the database admits. A parity case runs both over the same corpus.
77
+ */
78
+ export function projectNameKey(name) {
79
+ return name
80
+ .replace(/[\t\n\r]/gu, " ")
81
+ .replace(/ {2,}/gu, " ")
82
+ .replace(/^ | $/gu, "")
83
+ .replace(/[A-Z]+/gu, (run) => run.toLowerCase());
84
+ }
57
85
  export const projectSchema = z.object({
86
+ /** Null only for projects created before the control plane recorded dates. */
87
+ createdAt: timestampSchema.nullable(),
58
88
  id: projectIdSchema,
59
89
  name: projectNameSchema,
60
90
  organization: organizationIdSchema,
@@ -96,21 +126,30 @@ export const provisionedProjectSchema = z.object({
96
126
  * `kind`. Publishing an identifier no operation accepts invites a client to
97
127
  * build on it and then constrains us to keep it.
98
128
  *
99
- * `{id, name}` is exactly what the OAuth consent prompt already hands a browser,
100
- * and the framing there applies here word for word: this is a display aid rather
101
- * than an authorization control. Whatever the list shows, the authority for a
102
- * request is re-resolved from the member's organization when the request
103
- * arrives.
129
+ * This remains a display aid rather than an authorization control. Whatever the
130
+ * list shows, the authority for a request is re-resolved from the member's
131
+ * organization when the request arrives. Lifecycle state and version make an
132
+ * archived row manageable without making it a valid work target.
104
133
  */
105
134
  export const projectSummarySchema = z.object({
135
+ createdAt: timestampSchema.nullable(),
136
+ deletionCounts: z.object({
137
+ endpoints: z.number().int().nonnegative(),
138
+ keys: z.number().int().nonnegative(),
139
+ runs: z.number().int().nonnegative(),
140
+ schedules: z.number().int().nonnegative(),
141
+ }),
106
142
  environmentKinds: z
107
143
  .array(environmentKindSchema)
108
144
  .length(2, "a project has exactly two environments"),
109
145
  id: projectIdSchema,
110
146
  name: projectNameSchema,
147
+ status: projectStatusSchema.exclude(["deleted"]),
148
+ version: versionSchema,
111
149
  });
150
+ export const projectDeletionCountsSchema = projectSummarySchema.shape.deletionCounts;
112
151
  /**
113
- * Every project in the member's organization that they may act in.
152
+ * Every active or archived project in the member's organization.
114
153
  *
115
154
  * **Unpaged, with a stated ceiling, because the store beneath it is.**
116
155
  * `listProjects` reads an organization's projects in one go; declaring a page
@@ -125,6 +164,8 @@ export const projectListSchema = z.object({
125
164
  data: z
126
165
  .array(projectSummarySchema)
127
166
  .max(PROJECT_LIST_LIMIT, "more projects than this response can carry; paging is required"),
167
+ /** Active and archived projects occupy a plan slot; deleted projects do not. */
168
+ total: z.number().int().nonnegative().max(PROJECT_LIST_LIMIT),
128
169
  });
129
170
  /**
130
171
  * What a member sends to create a project.
@@ -136,10 +177,22 @@ export const projectListSchema = z.object({
136
177
  * a project observable without one of them is a state this product does not
137
178
  * have.
138
179
  *
139
- * There is no `status`: a project is created `active`, and a caller that could
140
- * create an archived one would be creating something it must then unarchive
141
- * through an operation that does not exist.
180
+ * There is no `status`: a project is created `active`; archiving is a separate
181
+ * administrator operation.
142
182
  */
143
183
  export const projectCreateRequestSchema = z.object({
144
184
  name: projectNameSchema,
145
185
  });
186
+ /** Rename the project without changing its opaque identifier. */
187
+ export const projectUpdateRequestSchema = z.object({
188
+ name: projectNameSchema,
189
+ });
190
+ /** Park or restore the complete project without rewriting its schedules. */
191
+ export const projectArchiveRequestSchema = z.object({
192
+ archived: z.boolean(),
193
+ });
194
+ /** What a successful permanent deletion removed. */
195
+ export const projectDeletedSchema = z.object({
196
+ deleted: z.literal(true),
197
+ destroyed: projectDeletionCountsSchema,
198
+ });