@antasphere/chassis-contract 0.0.0-stage → 1.0.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 (130) hide show
  1. package/LICENSE +82 -0
  2. package/dist/define.d.ts +177 -0
  3. package/dist/define.d.ts.map +1 -0
  4. package/dist/define.js +35 -0
  5. package/dist/define.js.map +1 -0
  6. package/dist/entitlements.d.ts +486 -0
  7. package/dist/entitlements.d.ts.map +1 -0
  8. package/dist/entitlements.js +344 -0
  9. package/dist/entitlements.js.map +1 -0
  10. package/dist/identity.d.ts +56 -0
  11. package/dist/identity.d.ts.map +1 -0
  12. package/dist/identity.js +2 -0
  13. package/dist/identity.js.map +1 -0
  14. package/dist/index.d.ts +23 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +29 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/routes/index.d.ts +5403 -0
  19. package/dist/routes/index.d.ts.map +1 -0
  20. package/dist/routes/index.js +940 -0
  21. package/dist/routes/index.js.map +1 -0
  22. package/dist/schemas/api-keys.d.ts +54 -0
  23. package/dist/schemas/api-keys.d.ts.map +1 -0
  24. package/dist/schemas/api-keys.js +48 -0
  25. package/dist/schemas/api-keys.js.map +1 -0
  26. package/dist/schemas/audit.d.ts +74 -0
  27. package/dist/schemas/audit.d.ts.map +1 -0
  28. package/dist/schemas/audit.js +65 -0
  29. package/dist/schemas/audit.js.map +1 -0
  30. package/dist/schemas/break-glass.d.ts +42 -0
  31. package/dist/schemas/break-glass.d.ts.map +1 -0
  32. package/dist/schemas/break-glass.js +38 -0
  33. package/dist/schemas/break-glass.js.map +1 -0
  34. package/dist/schemas/cli-auth.d.ts +59 -0
  35. package/dist/schemas/cli-auth.d.ts.map +1 -0
  36. package/dist/schemas/cli-auth.js +68 -0
  37. package/dist/schemas/cli-auth.js.map +1 -0
  38. package/dist/schemas/common.d.ts +96 -0
  39. package/dist/schemas/common.d.ts.map +1 -0
  40. package/dist/schemas/common.js +205 -0
  41. package/dist/schemas/common.js.map +1 -0
  42. package/dist/schemas/demo-passes.d.ts +90 -0
  43. package/dist/schemas/demo-passes.d.ts.map +1 -0
  44. package/dist/schemas/demo-passes.js +60 -0
  45. package/dist/schemas/demo-passes.js.map +1 -0
  46. package/dist/schemas/files.d.ts +39 -0
  47. package/dist/schemas/files.d.ts.map +1 -0
  48. package/dist/schemas/files.js +26 -0
  49. package/dist/schemas/files.js.map +1 -0
  50. package/dist/schemas/instance.d.ts +58 -0
  51. package/dist/schemas/instance.d.ts.map +1 -0
  52. package/dist/schemas/instance.js +70 -0
  53. package/dist/schemas/instance.js.map +1 -0
  54. package/dist/schemas/invitations.d.ts +100 -0
  55. package/dist/schemas/invitations.d.ts.map +1 -0
  56. package/dist/schemas/invitations.js +55 -0
  57. package/dist/schemas/invitations.js.map +1 -0
  58. package/dist/schemas/me.d.ts +81 -0
  59. package/dist/schemas/me.d.ts.map +1 -0
  60. package/dist/schemas/me.js +178 -0
  61. package/dist/schemas/me.js.map +1 -0
  62. package/dist/schemas/members.d.ts +60 -0
  63. package/dist/schemas/members.d.ts.map +1 -0
  64. package/dist/schemas/members.js +43 -0
  65. package/dist/schemas/members.js.map +1 -0
  66. package/dist/schemas/projects.d.ts +232 -0
  67. package/dist/schemas/projects.d.ts.map +1 -0
  68. package/dist/schemas/projects.js +140 -0
  69. package/dist/schemas/projects.js.map +1 -0
  70. package/dist/schemas/scope.d.ts +14 -0
  71. package/dist/schemas/scope.d.ts.map +1 -0
  72. package/dist/schemas/scope.js +5 -0
  73. package/dist/schemas/scope.js.map +1 -0
  74. package/dist/schemas/setup.d.ts +28 -0
  75. package/dist/schemas/setup.d.ts.map +1 -0
  76. package/dist/schemas/setup.js +28 -0
  77. package/dist/schemas/setup.js.map +1 -0
  78. package/dist/schemas/sso-connect.d.ts +30 -0
  79. package/dist/schemas/sso-connect.d.ts.map +1 -0
  80. package/dist/schemas/sso-connect.js +38 -0
  81. package/dist/schemas/sso-connect.js.map +1 -0
  82. package/dist/schemas/sso-logout.d.ts +17 -0
  83. package/dist/schemas/sso-logout.d.ts.map +1 -0
  84. package/dist/schemas/sso-logout.js +16 -0
  85. package/dist/schemas/sso-logout.js.map +1 -0
  86. package/dist/schemas/teams.d.ts +100 -0
  87. package/dist/schemas/teams.d.ts.map +1 -0
  88. package/dist/schemas/teams.js +84 -0
  89. package/dist/schemas/teams.js.map +1 -0
  90. package/dist/schemas/workspaces.d.ts +99 -0
  91. package/dist/schemas/workspaces.d.ts.map +1 -0
  92. package/dist/schemas/workspaces.js +96 -0
  93. package/dist/schemas/workspaces.js.map +1 -0
  94. package/dist/seams.d.ts +185 -0
  95. package/dist/seams.d.ts.map +1 -0
  96. package/dist/seams.js +37 -0
  97. package/dist/seams.js.map +1 -0
  98. package/dist/wire.d.ts +101 -0
  99. package/dist/wire.d.ts.map +1 -0
  100. package/dist/wire.js +161 -0
  101. package/dist/wire.js.map +1 -0
  102. package/package.json +41 -3
  103. package/src/define.ts +64 -0
  104. package/src/entitlements.ts +527 -0
  105. package/src/identity.ts +49 -0
  106. package/src/index.ts +38 -0
  107. package/src/routes/index.ts +1146 -0
  108. package/src/schemas/api-keys.ts +53 -0
  109. package/src/schemas/audit.ts +74 -0
  110. package/src/schemas/break-glass.ts +46 -0
  111. package/src/schemas/cli-auth.ts +78 -0
  112. package/src/schemas/common.ts +206 -0
  113. package/src/schemas/demo-passes.ts +73 -0
  114. package/src/schemas/files.ts +30 -0
  115. package/src/schemas/instance.ts +71 -0
  116. package/src/schemas/invitations.ts +66 -0
  117. package/src/schemas/me.ts +184 -0
  118. package/src/schemas/members.ts +53 -0
  119. package/src/schemas/projects.ts +174 -0
  120. package/src/schemas/scope.ts +18 -0
  121. package/src/schemas/setup.ts +31 -0
  122. package/src/schemas/sso-connect.ts +40 -0
  123. package/src/schemas/sso-logout.ts +17 -0
  124. package/src/schemas/teams.ts +101 -0
  125. package/src/schemas/workspaces.ts +109 -0
  126. package/src/seams.ts +202 -0
  127. package/src/wire.ts +246 -0
  128. package/test/define.test.ts +59 -0
  129. package/test/hub-wire.test.ts +114 -0
  130. package/README.md +0 -3
@@ -0,0 +1,486 @@
1
+ import { z } from 'zod';
2
+ import type { HeaderReader, Principal } from './seams.js';
3
+ /**
4
+ * The billing rail's declaration layer (the pay-per-use billing rail spec,
5
+ * §7 and §8b): a route says ONCE, next to its contract, what it meters, which
6
+ * limit it checks and which feature it needs. One chassis middleware reads
7
+ * the declaration and enforces it for every client of `/api/v1` at once (the
8
+ * dashboard, the CLI and the MCP tools are all HTTP clients of it), and a
9
+ * handler never checks or emits by hand.
10
+ *
11
+ * Client-safe on purpose: no Hono here. A route is referenced by its
12
+ * `{ method, path }` as the OpenAPI helper spells it (`{id}` params), so the
13
+ * tool's contract builds the registry from its route objects and the SDK can
14
+ * read the same declarations without pulling the server.
15
+ */
16
+ /**
17
+ * The plan tiers, a CLOSED enum the hub owns and this contract mirrors so a
18
+ * tool cannot invent one (§8b). `free` is the default with no subscription;
19
+ * `pro` is the paid plan. "Enterprise" is not a tier but a per-account
20
+ * override the hub serves.
21
+ */
22
+ export declare const ENTITLEMENT_TIERS: readonly ["free", "pro"];
23
+ export type EntitlementTier = (typeof ENTITLEMENT_TIERS)[number];
24
+ export declare const entitlementTierSchema: z.ZodEnum<{
25
+ free: "free";
26
+ pro: "pro";
27
+ }>;
28
+ /** The hub's name for the same closed enum (`BILLING_PLANS` in `@antasphere/contract`). */
29
+ export declare const BILLING_PLANS: readonly ["free", "pro"];
30
+ /**
31
+ * The one scope of the tool → hub machine channel (§6): a `client_credentials`
32
+ * token on the registry client, reaching `/usage/*` and nothing else.
33
+ */
34
+ export declare const USAGE_WRITE_SCOPE = "usage:write";
35
+ /**
36
+ * The error codes the three tool routes of the hub (`POST /usage/events`,
37
+ * `GET /usage/entitlements`, `POST /usage/check`) answer with, and the status
38
+ * each rides on: the hub's `USAGE_ROUTE_ERRORS` (PRDCT-2677).
39
+ * `tool_credential_required`: the caller is not a registry tool's machine
40
+ * credential. `unknown_account`: no organization holds the `accountRef` (the
41
+ * check and the entitlements read; the ingest answers it per event instead).
42
+ * `validation_error`: the body or the query does not parse. The chassis maps
43
+ * the check's two (`check.ts`): a 404 `unknown_account` and a 400 are the
44
+ * hub's judgement, allowed and logged, never an outage.
45
+ */
46
+ export declare const USAGE_ROUTE_ERRORS: {
47
+ readonly tool_credential_required: 401;
48
+ readonly unknown_account: 404;
49
+ readonly validation_error: 400;
50
+ };
51
+ export type UsageRouteError = keyof typeof USAGE_ROUTE_ERRORS;
52
+ /**
53
+ * The wire shape of one usage event as the hub ingests it: the hub's
54
+ * `usageEventSchema` (`packages/contract/src/schemas/billing.ts` of the hub,
55
+ * PRDCT-2625), copied here and checked against the hub's wire snapshot
56
+ * (`wire.ts`, `pnpm --filter @antasphere/chassis-contract wire:check`, the
57
+ * `hub-wire` CI job; PRDCT-2677), so the fake hub of the test kit refuses
58
+ * exactly what the real hub refuses (PRDCT-2629). `userId` is the HUB user
59
+ * (the SSO `sub`), never the tool's local id, and null when the actor is a
60
+ * resource owner the tool could not name; `accountRef` is the hub
61
+ * organization id (a uuid) the tool carries as its workspace's central
62
+ * account id. `toolSlug` is accepted by the hub only when it equals the
63
+ * calling token's registry slug — the chassis NEVER sends it: the token is
64
+ * the only authority on the tool's name, the body never names the tool
65
+ * (PRDCT-2629; `UsageEvent` in seams.ts has no such field).
66
+ */
67
+ export declare const usageEventSchema: z.ZodObject<{
68
+ id: z.ZodString;
69
+ actionKey: z.ZodOptional<z.ZodString>;
70
+ meter: z.ZodOptional<z.ZodString>;
71
+ quantity: z.ZodNumber;
72
+ unit: z.ZodString;
73
+ occurredAt: z.ZodISODateTime;
74
+ workspaceId: z.ZodString;
75
+ accountRef: z.ZodUUID;
76
+ userId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
77
+ via: z.ZodEnum<{
78
+ session: "session";
79
+ api_key: "api_key";
80
+ oauth: "oauth";
81
+ }>;
82
+ resourceType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
83
+ resourceId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
84
+ toolSlug: z.ZodOptional<z.ZodString>;
85
+ source: z.ZodObject<{
86
+ instanceId: z.ZodString;
87
+ edition: z.ZodString;
88
+ version: z.ZodString;
89
+ }, z.core.$strip>;
90
+ }, z.core.$strip>;
91
+ /** The batch cap of `POST /usage/events` (the hub's `USAGE_EVENTS_BATCH_MAX`): at most this many events per post. */
92
+ export declare const USAGE_EVENTS_BATCH_MAX = 500;
93
+ /**
94
+ * The body of `POST /usage/events` as the hub parses it (its
95
+ * `usageEventsBatchSchema`): one to `USAGE_EVENTS_BATCH_MAX` elements, each
96
+ * judged on its own against `usageEventSchema` afterwards.
97
+ */
98
+ export declare const usageEventsBatchSchema: z.ZodObject<{
99
+ events: z.ZodArray<z.ZodUnknown>;
100
+ }, z.core.$strip>;
101
+ export type UsageEventsBatch = z.infer<typeof usageEventsBatchSchema>;
102
+ /**
103
+ * Why the hub did not accept an event (its `USAGE_REJECT_REASONS`, a closed
104
+ * list): `unknown_account`, `unknown_user`, `tool_mismatch`, and
105
+ * `invalid_event` (the element does not parse, its `occurredAt` falls outside
106
+ * the window, or its price exceeds `Number.MAX_SAFE_INTEGER`; `details` names
107
+ * the field).
108
+ */
109
+ export declare const USAGE_REJECT_REASONS: readonly ["unknown_account", "unknown_user", "tool_mismatch", "invalid_event"];
110
+ export declare const usageRejectReasonSchema: z.ZodEnum<{
111
+ unknown_account: "unknown_account";
112
+ unknown_user: "unknown_user";
113
+ tool_mismatch: "tool_mismatch";
114
+ invalid_event: "invalid_event";
115
+ }>;
116
+ export type UsageRejectReason = z.infer<typeof usageRejectReasonSchema>;
117
+ /**
118
+ * One element's answer. Since phase 2 an `accepted` result carries the
119
+ * `credits` the hub debited for it (0 when the action has no price).
120
+ */
121
+ export declare const usageEventResultSchema: z.ZodObject<{
122
+ id: z.ZodNullable<z.ZodString>;
123
+ status: z.ZodEnum<{
124
+ accepted: "accepted";
125
+ duplicate: "duplicate";
126
+ rejected: "rejected";
127
+ }>;
128
+ reason: z.ZodOptional<z.ZodEnum<{
129
+ unknown_account: "unknown_account";
130
+ unknown_user: "unknown_user";
131
+ tool_mismatch: "tool_mismatch";
132
+ invalid_event: "invalid_event";
133
+ }>>;
134
+ details: z.ZodOptional<z.ZodUnknown>;
135
+ credits: z.ZodOptional<z.ZodNumber>;
136
+ }, z.core.$strip>;
137
+ export type UsageEventResult = z.infer<typeof usageEventResultSchema>;
138
+ /**
139
+ * The hub's answer to `POST /usage/events`: one result per element, in the
140
+ * batch's order, and the three counts (always sent).
141
+ */
142
+ export declare const usageIngestResultSchema: z.ZodObject<{
143
+ results: z.ZodArray<z.ZodObject<{
144
+ id: z.ZodNullable<z.ZodString>;
145
+ status: z.ZodEnum<{
146
+ accepted: "accepted";
147
+ duplicate: "duplicate";
148
+ rejected: "rejected";
149
+ }>;
150
+ reason: z.ZodOptional<z.ZodEnum<{
151
+ unknown_account: "unknown_account";
152
+ unknown_user: "unknown_user";
153
+ tool_mismatch: "tool_mismatch";
154
+ invalid_event: "invalid_event";
155
+ }>>;
156
+ details: z.ZodOptional<z.ZodUnknown>;
157
+ credits: z.ZodOptional<z.ZodNumber>;
158
+ }, z.core.$strip>>;
159
+ accepted: z.ZodNumber;
160
+ duplicate: z.ZodNumber;
161
+ rejected: z.ZodNumber;
162
+ }, z.core.$strip>;
163
+ export type UsageIngestResult = z.infer<typeof usageIngestResultSchema>;
164
+ /**
165
+ * The window an event's `occurredAt` must fall in, measured against the
166
+ * moment the hub receives it: the hub's `USAGE_EVENT_MAX_PAST_MS` and
167
+ * `USAGE_EVENT_MAX_FUTURE_MS` (`packages/contract/src/schemas/billing.ts`
168
+ * of the hub), same names and values, checked against the hub's wire
169
+ * snapshot (`wire.ts`, `wire:check`, the `hub-wire` CI job; PRDCT-2677). Backward, seven days:
170
+ * the queue's retry budget is about eight and a half hours, so an outage of
171
+ * a night is covered many times over, and a week also covers an operator
172
+ * re-driving held jobs by hand. Forward, five minutes: the skew every JWT
173
+ * verifier already tolerates; anything further is a clock that is wrong.
174
+ * Outside the window the hub answers `rejected(invalid_event)` naming
175
+ * `occurredAt` and never clamps, so the poster refuses BEFORE posting: an
176
+ * event the hub would refuse on its date is held where it was made, never
177
+ * counted as a rejection at the hub (PRDCT-2644).
178
+ */
179
+ export declare const USAGE_EVENT_MAX_PAST_MS: number;
180
+ export declare const USAGE_EVENT_MAX_FUTURE_MS: number;
181
+ /**
182
+ * Why an event's `occurredAt` is refused against the receipt time, or null
183
+ * when it falls inside the window (both bounds INCLUSIVE). The hub's
184
+ * `usageEventOccurrenceIssue`, copied (the wire snapshot's occurrence table
185
+ * checks it on the hub's offsets): the one comparison both
186
+ * halves run, the hub at ingest against its own clock, the chassis before
187
+ * posting against its best estimate of the receipt (its own clock), so the
188
+ * two never disagree on the sign or the edge.
189
+ */
190
+ export declare function usageEventOccurrenceIssue(occurredAt: Date, receivedAt: Date): {
191
+ path: 'occurredAt';
192
+ message: string;
193
+ } | null;
194
+ /**
195
+ * The request of `POST /usage/check` (the hub's `UsageCheckRequest`,
196
+ * PRDCT-2663): the paying account, the action key and the quantity of THIS
197
+ * request. `unit` is accepted and ignored by the hub, which prices by its
198
+ * own row; it is sent so the answer's unit can be compared and a mismatch
199
+ * logged.
200
+ */
201
+ export declare const usageCheckRequestSchema: z.ZodObject<{
202
+ accountRef: z.ZodUUID;
203
+ actionKey: z.ZodString;
204
+ quantity: z.ZodNumber;
205
+ unit: z.ZodOptional<z.ZodString>;
206
+ }, z.core.$strip>;
207
+ export type UsageCheckRequest = z.infer<typeof usageCheckRequestSchema>;
208
+ /**
209
+ * The three reasons a check refuses (`null` when allowed), the hub's
210
+ * `USAGE_CHECK_REASONS`: `account_suspended` (checked first, whatever the
211
+ * price), `unpriceable` (the price of this quantity exceeds
212
+ * `Number.MAX_SAFE_INTEGER`: the hub clamps `credits` to that bound, and the
213
+ * ingest would reject the same event on its `quantity`) and
214
+ * `insufficient_credits` (`balance < credits`). Each is a refusal, never an
215
+ * outage (PRDCT-2677).
216
+ */
217
+ export declare const USAGE_CHECK_REASONS: readonly ["insufficient_credits", "account_suspended", "unpriceable"];
218
+ export type UsageCheckReason = (typeof USAGE_CHECK_REASONS)[number];
219
+ /**
220
+ * The hub's answer to `POST /usage/check` (its `UsageCheck`, PRDCT-2663),
221
+ * checked against the hub's wire snapshot (PRDCT-2677): advisory, no side effect. `credits` is the price of
222
+ * this quantity from the price book row effective now (0 and `priced:
223
+ * false` when the tool has no row for the action: views are never priced,
224
+ * an unknown key is not a refusal); `balance` the account's; `allowed` is
225
+ * `balance >= credits` unless the organization is suspended, which refuses
226
+ * whatever the price. `topUpUrl` is always present: the hub's billing page
227
+ * for that organization, what a `402 entitlement_denied` carries straight
228
+ * off the answer. No lower bound on `quantity` or `credits`, as at the hub: a
229
+ * copy stricter than the hub reads a valid answer as an outage.
230
+ */
231
+ export declare const usageCheckSchema: z.ZodObject<{
232
+ accountRef: z.ZodUUID;
233
+ actionKey: z.ZodString;
234
+ quantity: z.ZodNumber;
235
+ allowed: z.ZodBoolean;
236
+ credits: z.ZodNumber;
237
+ balance: z.ZodNumber;
238
+ unit: z.ZodNullable<z.ZodString>;
239
+ priced: z.ZodBoolean;
240
+ plan: z.ZodEnum<{
241
+ free: "free";
242
+ pro: "pro";
243
+ }>;
244
+ reason: z.ZodNullable<z.ZodEnum<{
245
+ insufficient_credits: "insufficient_credits";
246
+ account_suspended: "account_suspended";
247
+ unpriceable: "unpriceable";
248
+ }>>;
249
+ topUpUrl: z.ZodString;
250
+ }, z.core.$strip>;
251
+ export type UsageCheck = z.infer<typeof usageCheckSchema>;
252
+ /** The wire code of a credit refusal (§7 step 4), and its `details`. */
253
+ export declare const ENTITLEMENT_DENIED = "entitlement_denied";
254
+ export interface EntitlementDeniedDetails {
255
+ /** The price of this action, in credits. */
256
+ credits: number;
257
+ /** The organization's balance, in credits. */
258
+ balance: number;
259
+ /** The hub's billing page for the organization: where a human adds credits. */
260
+ topUpUrl: string;
261
+ }
262
+ /**
263
+ * What a declaration's functions see: the request as the chassis reads it.
264
+ * `audit` is present only AFTER the handler ran (the emit), carrying what the
265
+ * handler recorded for its audit row — the place a stored size or a created
266
+ * resource id lives.
267
+ */
268
+ export interface EntitlementRequest {
269
+ method: string;
270
+ path: string;
271
+ headers: HeaderReader;
272
+ /** Route params by name (`{id}` → its value). */
273
+ params: Record<string, string>;
274
+ principal: Principal | null;
275
+ /**
276
+ * The parsed JSON body of the request, read once and shared with the
277
+ * handler's own validation (the server caches the parse); `undefined`
278
+ * when the request carries none or it does not parse (the route's
279
+ * validator then answers 400 itself). A count limit or a conditional
280
+ * feature reads what the request asks for here (PRDCT-2702): the mint
281
+ * of a share link with a password, the address an invitation names.
282
+ */
283
+ body: () => Promise<unknown>;
284
+ audit?: {
285
+ action: string;
286
+ resourceType: string;
287
+ resourceId?: string;
288
+ metadata?: Record<string, unknown>;
289
+ } | undefined;
290
+ }
291
+ /**
292
+ * Who pays and who is reported (§8): the organization's account pays, the
293
+ * user is reported. The default actor is the principal; a route on an
294
+ * anonymous surface resolves its resource's owner here instead (share token
295
+ * → deck → owner → the owner's workspace and account) — the viewer is never
296
+ * identified.
297
+ */
298
+ export interface ActorRef {
299
+ /** The reported user, or null when the actor is a resource owner the tool could not name. */
300
+ userId: string | null;
301
+ workspaceId: string;
302
+ /** The paying account (the workspace's central account id); absent = unmetered (oss, or a cloud-local workspace). */
303
+ accountRef?: string | undefined;
304
+ }
305
+ /**
306
+ * The actor of a request when it is not the principal: the owner of a
307
+ * resource reached anonymously (a form response through a share link), or
308
+ * the workspace a public door opens (an invitation accepted, a collaborator
309
+ * grant claimed). Resolves null to leave the route to its own handling.
310
+ */
311
+ export type ActorHook = (ctx: EntitlementRequest) => Promise<ActorRef | null> | ActorRef | null;
312
+ export interface MeterDeclaration {
313
+ /** The action key, namespaced by the tool's price book (`things.publish`). */
314
+ key: string;
315
+ /** The unit the price is per (`call`, `bytes`, `items`). */
316
+ unit: string;
317
+ /** The quantity of this request; default 1. Called before the handler (the check) and after it (the emit, with `audit` set). */
318
+ quantity?: (ctx: EntitlementRequest) => number;
319
+ /** The actor when it is not the principal (the owner of a resource reached anonymously). */
320
+ actor?: ActorHook;
321
+ }
322
+ export interface LimitDeclaration {
323
+ /** The limit key (`things.maxBytes`), declared with its per-tier values in the tool's `entitlements` slot. */
324
+ key: string;
325
+ /**
326
+ * The value this request observes, compared against the tier's value; over
327
+ * it is a plan refusal. A SIZE limit reads the declared length
328
+ * (`declaredContentLength`, synchronous: the gate then also demands a
329
+ * Content-Length on a metered account). A COUNT limit (PRDCT-2702) may be
330
+ * asynchronous and read the body and the tool's own tables: it returns the
331
+ * count AFTER this action (the resource's things plus this one), so
332
+ * `observed > max` is the refusal, and `null` when there is nothing to
333
+ * judge (the resource does not resolve, the caller may not see it): the
334
+ * gate then lets the route answer on its own.
335
+ */
336
+ value: (ctx: EntitlementRequest) => number | null | Promise<number | null>;
337
+ }
338
+ /**
339
+ * A feature a route needs, with an optional condition on the request
340
+ * (PRDCT-2702): a route that creates a thing needs `things.premium` only
341
+ * when the body asks for the premium option, so a declaration on the route
342
+ * refuses the act and never the route. Absent `when`, the whole route needs
343
+ * the feature.
344
+ */
345
+ export interface FeatureDeclaration {
346
+ key: string;
347
+ when?: (ctx: EntitlementRequest) => boolean | Promise<boolean>;
348
+ }
349
+ export interface RouteEntitlement {
350
+ meter?: MeterDeclaration | undefined;
351
+ limit?: LimitDeclaration | undefined;
352
+ /** A feature key (`things.premium`), declared with its per-tier switch in the tool's `entitlements` slot, or the key with a condition. */
353
+ feature?: string | FeatureDeclaration | undefined;
354
+ /**
355
+ * The actor of a route that meters nothing but checks a limit or a feature
356
+ * on a PUBLIC door (no principal): the workspace the door opens pays and is
357
+ * judged. A meter's own `actor` serves the same purpose on a metered route;
358
+ * this one covers the limit-only shape. Either makes the route a viewer's
359
+ * surface: every refusal to it is one neutral sentence.
360
+ */
361
+ actor?: ActorHook | undefined;
362
+ }
363
+ /** The key of a route's feature declaration, whichever shape it uses; null when none. */
364
+ export declare function featureKeyOf(entry: RouteEntitlement): string | null;
365
+ /** The actor hook of a route, the entry's own or its meter's; null when the principal is the actor. */
366
+ export declare function actorHookOf(entry: RouteEntitlement): ActorHook | null;
367
+ /** A route as the contract spells it: the OpenAPI method and the `{param}` path. */
368
+ export interface RouteRef {
369
+ method: string;
370
+ path: string;
371
+ }
372
+ export interface RouteEntitlementEntry extends RouteEntitlement {
373
+ route: RouteRef;
374
+ }
375
+ /** `METHOD /path` — the registry key of a route. */
376
+ export declare function routeEntitlementKey(route: RouteRef): string;
377
+ export type RouteEntitlementDeclarations = ReadonlyMap<string, RouteEntitlementEntry>;
378
+ /**
379
+ * Build the registry from the tool's route objects. One entry per route: a
380
+ * second declaration of the same route throws at module load, so a typo or a
381
+ * copy cannot silently double or replace a price.
382
+ */
383
+ export declare function declareRouteEntitlements(entries: ReadonlyArray<RouteEntitlementEntry>): RouteEntitlementDeclarations;
384
+ /** The declared body size of a request (its `Content-Length`), 0 when absent or malformed. */
385
+ export declare function declaredContentLength(ctx: EntitlementRequest): number;
386
+ /**
387
+ * The stored size a handler recorded in its audit metadata (`sizeBytes`),
388
+ * falling back to the declared body size before the handler ran. The check
389
+ * sees the declared size, the emit sees the bytes that were actually kept.
390
+ */
391
+ export declare function auditedSizeBytes(ctx: EntitlementRequest): number;
392
+ /**
393
+ * A priced action: the default the hub seeds its price book from (§7); the
394
+ * hub's rows win afterwards. `creditsPerUnit` credits buy `per` units of
395
+ * `unit` (`per` defaults to 1): an action metered in bytes and priced per
396
+ * mebibyte declares `{ creditsPerUnit: 5, unit: 'bytes', per: 1048576 }`,
397
+ * so the meter stays exact, the price reads as the seed table says it, and
398
+ * the two agree by construction (PRDCT-2627: the boot refuses a route whose
399
+ * meter unit differs from its action's). The hub prices an event as
400
+ * `creditsPerUnit × quantity / per`; rounding a fraction of a credit is the
401
+ * hub's rule, not the tool's.
402
+ */
403
+ export declare const entitlementActionSchema: z.ZodObject<{
404
+ key: z.ZodString;
405
+ creditsPerUnit: z.ZodNumber;
406
+ unit: z.ZodString;
407
+ per: z.ZodOptional<z.ZodNumber>;
408
+ label: z.ZodString;
409
+ }, z.core.$strip>;
410
+ export type EntitlementAction = z.infer<typeof entitlementActionSchema>;
411
+ /**
412
+ * The credits an action declares for a quantity: `creditsPerUnit × quantity
413
+ * / per`, exact (a fraction is the hub's to round). A test pins the seed
414
+ * values with it, so a unit slip (5 per byte where 5 per MB was meant)
415
+ * fails before the price book is seeded from discovery.
416
+ */
417
+ export declare function declaredCredits(action: Pick<EntitlementAction, 'creditsPerUnit' | 'per'>, quantity: number): number;
418
+ /** A limit's value per tier plus the oss value (the operator's own knob); null = unlimited. */
419
+ export declare const tierLimitSchema: z.ZodObject<{
420
+ oss: z.ZodNullable<z.ZodNumber>;
421
+ free: z.ZodNullable<z.ZodNumber>;
422
+ pro: z.ZodNullable<z.ZodNumber>;
423
+ }, z.core.$strip>;
424
+ export type TierLimit = z.infer<typeof tierLimitSchema>;
425
+ /** A feature's switch per tier (oss has every feature: a self-hosted instance knows no plan). */
426
+ export declare const tierFeatureSchema: z.ZodObject<{
427
+ free: z.ZodBoolean;
428
+ pro: z.ZodBoolean;
429
+ }, z.core.$strip>;
430
+ export type TierFeature = z.infer<typeof tierFeatureSchema>;
431
+ export declare const toolEntitlementsSchema: z.ZodObject<{
432
+ actions: z.ZodArray<z.ZodObject<{
433
+ key: z.ZodString;
434
+ creditsPerUnit: z.ZodNumber;
435
+ unit: z.ZodString;
436
+ per: z.ZodOptional<z.ZodNumber>;
437
+ label: z.ZodString;
438
+ }, z.core.$strip>>;
439
+ limits: z.ZodRecord<z.ZodString, z.ZodObject<{
440
+ oss: z.ZodNullable<z.ZodNumber>;
441
+ free: z.ZodNullable<z.ZodNumber>;
442
+ pro: z.ZodNullable<z.ZodNumber>;
443
+ }, z.core.$strip>>;
444
+ features: z.ZodRecord<z.ZodString, z.ZodObject<{
445
+ free: z.ZodBoolean;
446
+ pro: z.ZodBoolean;
447
+ }, z.core.$strip>>;
448
+ }, z.core.$strip>;
449
+ export type ToolEntitlements = z.infer<typeof toolEntitlementsSchema>;
450
+ /**
451
+ * The hub's answer to `GET /usage/entitlements?accountRef=` (§5): the hub's
452
+ * `usageEntitlementsSchema` (PRDCT-2636), checked against the hub's wire
453
+ * snapshot (PRDCT-2677). The account's
454
+ * plan, resolved from the tier's defaults and the account's overrides; in
455
+ * phase 1 `limits` and `features` are empty and the tool's own declared
456
+ * values fill the numbers. A limit's value is a number or a boolean: the
457
+ * hub may switch a numeric limit off or on per account (`true` = unlimited,
458
+ * `false` = nothing allowed; `profiles.ts` resolves it so). Since phase 2
459
+ * (PRDCT-2663) the hub serves its real rows — every limit key it holds for
460
+ * the calling tool and the features on for the account — and `upgradeUrl`,
461
+ * its upgrade page for the organization carrying `org`, `tool` and `plan`;
462
+ * the chassis appends `&key=<key>&requiredPlan=<tier>` at refusal time.
463
+ * Required: every hub since 0.11.0 sends it.
464
+ */
465
+ export declare const entitlementProfileSchema: z.ZodObject<{
466
+ accountRef: z.ZodUUID;
467
+ plan: z.ZodEnum<{
468
+ free: "free";
469
+ pro: "pro";
470
+ }>;
471
+ planUntil: z.ZodNullable<z.ZodString>;
472
+ limits: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodBoolean]>>;
473
+ features: z.ZodArray<z.ZodString>;
474
+ upgradeUrl: z.ZodString;
475
+ }, z.core.$strip>;
476
+ export type EntitlementProfile = z.infer<typeof entitlementProfileSchema>;
477
+ /** The wire code of a plan refusal, and its `details` (§7 step 2). */
478
+ export declare const PLAN_REQUIRED = "plan_required";
479
+ export interface PlanRequiredDetails {
480
+ key: string;
481
+ plan: EntitlementTier;
482
+ /** The first tier that allows the action, or null when none does. */
483
+ requiredPlan: EntitlementTier | null;
484
+ upgradeUrl: string;
485
+ }
486
+ //# sourceMappingURL=entitlements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../src/entitlements.ts"],"names":[],"mappings":"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"}