@cloudpdf/contract 3.0.0-next.1 → 3.0.0-next.3

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.
package/dist/openapi.js CHANGED
@@ -19,6 +19,14 @@ import {
19
19
  var sha256Hex = /^[0-9a-f]{64}$/i;
20
20
  var docIdPattern = /^[A-Za-z0-9_-]+$/;
21
21
  var tenantIdPattern = /^[A-Za-z0-9_-]+$/;
22
+ var originPattern = /^https?:\/\/(\*\.)?[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*(:\d{1,5})?$/;
23
+ var originPatternArray = z.array(
24
+ z.string().max(262).regex(
25
+ originPattern,
26
+ "must be a web origin like https://example.com (optionally one leading *. wildcard label, optional port, no path)"
27
+ )
28
+ ).min(1).max(32);
29
+ var shareTokenPattern = /^shr_[A-Za-z0-9_-]{24}$/;
22
30
  var adminWirePaths = {
23
31
  tenants: "/v1/tenants",
24
32
  tenant: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}`,
@@ -26,16 +34,28 @@ var adminWirePaths = {
26
34
  documentsInit: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/init`,
27
35
  document: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}`,
28
36
  documentCommit: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/commit`,
29
- documentUploadDirect: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/upload-direct`,
37
+ documentUploadProxy: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/upload-proxy`,
30
38
  documentDownload: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/download`,
31
39
  /** The warmed dashboard-tile artifact. Returns 404 while `pending`/`locked`. */
32
40
  documentThumbnail: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/thumbnail`,
33
41
  tokenIssue: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/tokens`,
34
42
  tokenRevoke: (tenantId, jti) => `/v1/tenants/${encodeURIComponent(tenantId)}/tokens/${encodeURIComponent(jti)}/revoke`,
43
+ shares: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/shares`,
44
+ share: (tenantId, shareId) => `/v1/tenants/${encodeURIComponent(tenantId)}/shares/${encodeURIComponent(shareId)}`,
45
+ tenantUsage: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/usage`,
46
+ tenantSuspend: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/suspend`,
47
+ tenantResume: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/resume`,
48
+ /**
49
+ * The public share-session exchange. Deliberately outside any tenant
50
+ * subtree: the caller holds only a share token and does not know the
51
+ * tenant — the grant row resolves it server-side.
52
+ */
53
+ shareSessions: "/v1/share-sessions",
35
54
  /** Deployment-global singletons: API-token only, no tenant context. */
36
55
  deploymentLicenseStatus: "/v1/deployment/license/status"
37
56
  };
38
57
  var DedupModeSchema = z.enum(["always-create", "reuse-existing"]);
58
+ var UploadPreferenceSchema = z.enum(["auto", "presigned", "proxy"]);
39
59
  var DocumentStateSchema = z.enum(["pending", "ready", "failed", "deleting"]);
40
60
  var AdminDocumentRecordSchema = z.object({
41
61
  id: z.string(),
@@ -60,7 +80,8 @@ var AdminDocumentInitRequestSchema = z.object({
60
80
  idempotencyKey: z.string().optional(),
61
81
  dedupMode: DedupModeSchema.optional(),
62
82
  docId: z.string().regex(docIdPattern).optional(),
63
- uploadTtlSec: z.number().finite().min(60).max(3600).optional()
83
+ uploadTtlSec: z.number().finite().min(60).max(3600).optional(),
84
+ uploadPreference: UploadPreferenceSchema.optional()
64
85
  });
65
86
  var AdminPresignedUploadSchema = z.object({
66
87
  url: z.string(),
@@ -75,7 +96,7 @@ var AdminInitUploadSchema = z.discriminatedUnion("kind", [
75
96
  key: z.string()
76
97
  }),
77
98
  z.object({
78
- kind: z.literal("direct"),
99
+ kind: z.literal("proxy"),
79
100
  url: z.string(),
80
101
  key: z.string()
81
102
  })
@@ -121,7 +142,7 @@ var AdminDocumentListResponseSchema = z.object({
121
142
  */
122
143
  nextCursor: z.string().nullable().optional()
123
144
  });
124
- var AdminUploadDirectResponseSchema = z.object({
145
+ var AdminUploadProxyResponseSchema = z.object({
125
146
  sha256: z.string().regex(sha256Hex)
126
147
  });
127
148
  var AdminErrorPayloadSchema = z.object({
@@ -168,6 +189,13 @@ var AdminTenantRecordSchema = z.object({
168
189
  name: z.string(),
169
190
  /** True when the namespace materialized on first use rather than via explicit create. */
170
191
  autoProvisioned: z.boolean(),
192
+ /**
193
+ * `suspended` tenants fail closed: every tenant JWT, doc JWT, and
194
+ * share exchange is refused until resume. The API token is exempt —
195
+ * the operator must be able to inspect, resume, or delete a
196
+ * suspended tenant. Optional because older servers omit it.
197
+ */
198
+ status: z.enum(["active", "suspended"]).optional(),
171
199
  createdAt: z.number()
172
200
  });
173
201
  var AdminTenantCreateRequestSchema = z.object({
@@ -197,7 +225,8 @@ var adminTenantScopes = [
197
225
  "docs.read",
198
226
  "docs.delete",
199
227
  "tokens.issue-doc",
200
- "tokens.revoke"
228
+ "tokens.revoke",
229
+ "shares.manage"
201
230
  ];
202
231
  var AdminTokenIssueDocRequestSchema = z.object({
203
232
  kind: z.literal("doc"),
@@ -215,6 +244,14 @@ var AdminTokenIssueDocRequestSchema = z.object({
215
244
  displayName: z.string().max(256).optional(),
216
245
  groupId: z.string().max(256).optional(),
217
246
  groups: z.array(z.string().max(256)).max(64).optional(),
247
+ /**
248
+ * Origin lock: web origins (scheme + host, optional port; one leading
249
+ * `*.` wildcard label allowed) the minted token may be presented
250
+ * from. Enforced on every engine request that carries a browser
251
+ * `Origin` header — hotlink prevention, not DRM: non-browser callers
252
+ * are governed by the token itself.
253
+ */
254
+ origins: originPatternArray.optional(),
218
255
  /** Token lifetime in seconds. */
219
256
  expiresIn: z.number().int().min(60).max(60 * 60 * 24 * 90)
220
257
  });
@@ -235,9 +272,110 @@ var AdminTokenIssueResponseSchema = z.object({
235
272
  /** Unix seconds. */
236
273
  expiresAt: z.number()
237
274
  });
275
+ var ShareGrantRecordSchema = z.object({
276
+ /** Grant id; doubles as the public share token (`shr_…`). */
277
+ id: z.string(),
278
+ tenantId: z.string(),
279
+ docId: z.string(),
280
+ layerName: z.string(),
281
+ /** Doc capabilities the exchanged session carries. */
282
+ scope: z.array(z.string()),
283
+ /** Origin allowlist; null means any origin. */
284
+ origins: z.array(z.string()).nullable(),
285
+ /** True when a passphrase is required at exchange. The passphrase itself is never returned. */
286
+ passwordProtected: z.boolean(),
287
+ /** Lifetime of each exchanged session token, in seconds. */
288
+ sessionTtlSeconds: z.number(),
289
+ /** Paused grants refuse exchange (404) but keep their configuration. */
290
+ disabled: z.boolean(),
291
+ /** Unix epoch ms after which exchange returns 410; null = no expiry. */
292
+ expiresAt: z.number().nullable(),
293
+ /** Total successful exchanges — a dashboard convenience, not the usage meter. */
294
+ exchangeCount: z.number(),
295
+ lastExchangedAt: z.number().nullable(),
296
+ createdBy: z.string(),
297
+ createdAt: z.number(),
298
+ updatedAt: z.number()
299
+ });
300
+ var ShareGrantCreateRequestSchema = z.object({
301
+ docId: z.string().regex(docIdPattern),
302
+ layerName: z.string().min(1).max(256).optional(),
303
+ /**
304
+ * Doc capability scopes the exchanged session will carry. Validated
305
+ * server-side against the engine's scope vocabulary, same as
306
+ * `tokens.issue`.
307
+ */
308
+ scope: z.array(z.string().min(1).max(128)).min(1).max(64),
309
+ /** Origin allowlist; omit to allow any origin. */
310
+ origins: originPatternArray.optional(),
311
+ /** Optional passphrase gate, checked at exchange. Stored as a hash; never returned. */
312
+ password: z.string().min(1).max(1024).optional(),
313
+ /** Lifetime of exchanged session tokens. Default 600. */
314
+ sessionTtlSeconds: z.number().int().min(60).max(60 * 60).optional(),
315
+ /** Unix epoch ms after which the grant stops exchanging; omit for no expiry. */
316
+ expiresAt: z.number().int().positive().optional()
317
+ });
318
+ var ShareGrantUpdateRequestSchema = z.object({
319
+ scope: z.array(z.string().min(1).max(128)).min(1).max(64).optional(),
320
+ origins: originPatternArray.nullable().optional(),
321
+ password: z.string().min(1).max(1024).nullable().optional(),
322
+ sessionTtlSeconds: z.number().int().min(60).max(60 * 60).optional(),
323
+ disabled: z.boolean().optional(),
324
+ expiresAt: z.number().int().positive().nullable().optional()
325
+ });
326
+ var ShareGrantResponseSchema = z.object({
327
+ share: ShareGrantRecordSchema
328
+ });
329
+ var ShareGrantListQuerySchema = z.object({
330
+ limit: z.coerce.number().int().min(1).max(ADMIN_DOCUMENT_LIST_MAX_LIMIT).default(ADMIN_DOCUMENT_LIST_DEFAULT_LIMIT),
331
+ cursor: z.string().min(1).optional(),
332
+ /** Filter to one document's grants. */
333
+ docId: z.string().regex(docIdPattern).optional()
334
+ });
335
+ var ShareGrantListResponseSchema = z.object({
336
+ shares: z.array(ShareGrantRecordSchema),
337
+ /** Opaque cursor for the next page; `null` on the last page. */
338
+ nextCursor: z.string().nullable().optional()
339
+ });
340
+ var AdminTenantShareParamsSchema = z.object({
341
+ tenantId: z.string().regex(tenantIdPattern),
342
+ shareId: z.string().regex(shareTokenPattern)
343
+ });
344
+ var ShareExchangeRequestSchema = z.object({
345
+ shareToken: z.string().regex(shareTokenPattern),
346
+ /** Required when the grant is passphrase-protected (422 otherwise). */
347
+ password: z.string().min(1).max(1024).optional()
348
+ });
349
+ var ShareSessionResponseSchema = z.object({
350
+ /** A doc-scoped session JWT carrying the grant's capabilities and origin lock. */
351
+ token: z.string(),
352
+ docId: z.string(),
353
+ layerName: z.string(),
354
+ /** Unix seconds when the session token expires; exchange again for a fresh one. */
355
+ expiresAt: z.number()
356
+ });
357
+ var TenantUsageQuerySchema = z.object({
358
+ /** UTC month to report, `YYYY-MM`. Defaults to the current month. */
359
+ period: z.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional()
360
+ });
361
+ var TenantUsageResponseSchema = z.object({
362
+ tenantId: z.string(),
363
+ periodStart: z.string(),
364
+ periodEnd: z.string(),
365
+ metrics: z.object({
366
+ "pdf.views": z.number(),
367
+ "pdf.uploads": z.number(),
368
+ "storage.bytes": z.number()
369
+ })
370
+ });
371
+ var TenantSuspendRequestSchema = z.object({
372
+ /** Optional operator reason, written to the security-events trail. */
373
+ reason: z.string().max(1024).optional()
374
+ });
238
375
  var adminOperations = {
239
376
  "tenants.create": {
240
377
  operationId: "tenants.create",
378
+ title: "Create tenant",
241
379
  summary: "Create a tenant, or confirm it already exists \u2014 ensure-style, idempotent.",
242
380
  method: "POST",
243
381
  path: adminWirePaths.tenants,
@@ -251,6 +389,7 @@ var adminOperations = {
251
389
  },
252
390
  "tenants.list": {
253
391
  operationId: "tenants.list",
392
+ title: "List tenants",
254
393
  summary: "List tenants, newest first, cursor-paginated.",
255
394
  method: "GET",
256
395
  path: adminWirePaths.tenants,
@@ -264,6 +403,7 @@ var adminOperations = {
264
403
  },
265
404
  "tenants.get": {
266
405
  operationId: "tenants.get",
406
+ title: "Get tenant",
267
407
  summary: "Fetch one tenant record.",
268
408
  method: "GET",
269
409
  path: "/v1/tenants/:tenantId",
@@ -277,6 +417,7 @@ var adminOperations = {
277
417
  },
278
418
  "tenants.delete": {
279
419
  operationId: "tenants.delete",
420
+ title: "Delete tenant",
280
421
  summary: "Delete a tenant and everything under it.",
281
422
  method: "DELETE",
282
423
  path: "/v1/tenants/:tenantId",
@@ -291,6 +432,7 @@ var adminOperations = {
291
432
  },
292
433
  "documents.init": {
293
434
  operationId: "documents.init",
435
+ title: "Initialize upload",
294
436
  summary: "Begin an upload: create (or resume/dedupe) a pending document and issue upload access.",
295
437
  method: "POST",
296
438
  path: "/v1/tenants/:tenantId/documents/init",
@@ -305,6 +447,7 @@ var adminOperations = {
305
447
  },
306
448
  "documents.commit": {
307
449
  operationId: "documents.commit",
450
+ title: "Commit upload",
308
451
  summary: "Finish an upload: verify the SHA-256 and promote the document to ready.",
309
452
  method: "POST",
310
453
  path: "/v1/tenants/:tenantId/documents/:id/commit",
@@ -318,24 +461,28 @@ var adminOperations = {
318
461
  404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
319
462
  }
320
463
  },
321
- "documents.uploadDirect": {
322
- operationId: "documents.uploadDirect",
323
- summary: "Upload the PDF bytes through the origin instead of a presigned URL.",
464
+ "documents.uploadProxy": {
465
+ operationId: "documents.uploadProxy",
466
+ title: "Upload document through the origin",
467
+ summary: "Upload a bounded PDF through the API when the initialized transfer kind is proxy.",
324
468
  method: "POST",
325
- path: "/v1/tenants/:tenantId/documents/:id/upload-direct",
469
+ path: "/v1/tenants/:tenantId/documents/:id/upload-proxy",
326
470
  credentials: ["api-token", "tenant-jwt"],
327
471
  scope: ["docs.create"],
328
472
  params: AdminTenantDocParamsSchema,
329
473
  body: {
330
- contentType: ["application/pdf", "application/octet-stream", "multipart/form-data"]
474
+ contentType: "multipart/form-data"
331
475
  },
332
476
  responses: {
333
- 200: { contentType: "application/json", schema: AdminUploadDirectResponseSchema },
334
- 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
335
- }
477
+ 200: { contentType: "application/json", schema: AdminUploadProxyResponseSchema },
478
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema },
479
+ 409: { contentType: "application/json", schema: AdminErrorPayloadSchema }
480
+ },
481
+ notes: "This bounded origin-mediated fallback must only be used after documents.init returns upload.kind=proxy. Auto mode prefers a presigned object-store PUT whenever available."
336
482
  },
337
483
  "documents.list": {
338
484
  operationId: "documents.list",
485
+ title: "List documents",
339
486
  summary: "List documents in the tenant, newest first, cursor-paginated.",
340
487
  method: "GET",
341
488
  path: "/v1/tenants/:tenantId/documents",
@@ -350,6 +497,7 @@ var adminOperations = {
350
497
  },
351
498
  "documents.get": {
352
499
  operationId: "documents.get",
500
+ title: "Get document",
353
501
  summary: "Fetch one document record.",
354
502
  method: "GET",
355
503
  path: "/v1/tenants/:tenantId/documents/:id",
@@ -364,6 +512,7 @@ var adminOperations = {
364
512
  },
365
513
  "documents.download": {
366
514
  operationId: "documents.download",
515
+ title: "Download document",
367
516
  summary: "Download the stored base PDF bytes.",
368
517
  method: "GET",
369
518
  path: "/v1/tenants/:tenantId/documents/:id/download",
@@ -378,6 +527,7 @@ var adminOperations = {
378
527
  },
379
528
  "documents.delete": {
380
529
  operationId: "documents.delete",
530
+ title: "Delete document",
381
531
  summary: "Delete a document and its stored artifacts.",
382
532
  method: "DELETE",
383
533
  path: "/v1/tenants/:tenantId/documents/:id",
@@ -392,6 +542,7 @@ var adminOperations = {
392
542
  },
393
543
  "documents.thumbnail": {
394
544
  operationId: "documents.thumbnail",
545
+ title: "Get thumbnail",
395
546
  summary: "Fetch the warmed dashboard-tile render for a document.",
396
547
  method: "GET",
397
548
  path: "/v1/tenants/:tenantId/documents/:id/thumbnail",
@@ -405,6 +556,7 @@ var adminOperations = {
405
556
  },
406
557
  "deployment.licenseStatus": {
407
558
  operationId: "deployment.licenseStatus",
559
+ title: "License status",
408
560
  summary: "License decision plus usage-reporting and meter snapshots for this deployment.",
409
561
  method: "GET",
410
562
  path: adminWirePaths.deploymentLicenseStatus,
@@ -416,6 +568,7 @@ var adminOperations = {
416
568
  },
417
569
  "tokens.issue": {
418
570
  operationId: "tokens.issue",
571
+ title: "Issue token",
419
572
  summary: "Mint a delegated JWT: a doc token, or (API token only) a tenant token.",
420
573
  method: "POST",
421
574
  path: "/v1/tenants/:tenantId/tokens",
@@ -433,6 +586,7 @@ var adminOperations = {
433
586
  },
434
587
  "tokens.revoke": {
435
588
  operationId: "tokens.revoke",
589
+ title: "Revoke token",
436
590
  summary: "Revoke a token by jti; live sessions drop on their next heartbeat.",
437
591
  method: "POST",
438
592
  path: "/v1/tenants/:tenantId/tokens/:jti/revoke",
@@ -449,6 +603,153 @@ var adminOperations = {
449
603
  400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
450
604
  },
451
605
  notes: "Mounted only when the deployment enables token revocation."
606
+ },
607
+ "tenants.usage": {
608
+ operationId: "tenants.usage",
609
+ title: "Tenant usage",
610
+ summary: "Per-tenant usage facts (views, uploads, stored bytes) for one UTC month.",
611
+ method: "GET",
612
+ path: "/v1/tenants/:tenantId/usage",
613
+ credentials: ["api-token", "tenant-jwt"],
614
+ scope: ["docs.read"],
615
+ params: AdminTenantParamsSchema,
616
+ query: TenantUsageQuerySchema,
617
+ responses: {
618
+ 200: { contentType: "application/json", schema: TenantUsageResponseSchema },
619
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema },
620
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
621
+ },
622
+ notes: "Facts only \u2014 no limits or billing state. Views count share exchanges plus authorized /v1/access grants, deduplicated across the two."
623
+ },
624
+ "tenants.suspend": {
625
+ operationId: "tenants.suspend",
626
+ title: "Suspend tenant",
627
+ summary: "Fail the tenant closed: refuse every tenant JWT, doc JWT, and share exchange.",
628
+ method: "POST",
629
+ path: "/v1/tenants/:tenantId/suspend",
630
+ credentials: ["api-token"],
631
+ scope: [],
632
+ params: AdminTenantParamsSchema,
633
+ body: {
634
+ contentType: "application/json",
635
+ schema: TenantSuspendRequestSchema,
636
+ required: false
637
+ },
638
+ responses: {
639
+ 204: {},
640
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
641
+ },
642
+ notes: "Instantly reversible with resume. The API token is exempt, so a suspended tenant can still be inspected, exported, resumed, or deleted."
643
+ },
644
+ "tenants.resume": {
645
+ operationId: "tenants.resume",
646
+ title: "Resume tenant",
647
+ summary: "Lift a suspension; credentials for the tenant verify again.",
648
+ method: "POST",
649
+ path: "/v1/tenants/:tenantId/resume",
650
+ credentials: ["api-token"],
651
+ scope: [],
652
+ params: AdminTenantParamsSchema,
653
+ responses: {
654
+ 204: {},
655
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
656
+ }
657
+ },
658
+ "shares.create": {
659
+ operationId: "shares.create",
660
+ title: "Create share",
661
+ summary: "Create a standing share grant for one document \u2014 the no-backend embed credential.",
662
+ method: "POST",
663
+ path: "/v1/tenants/:tenantId/shares",
664
+ credentials: ["api-token", "tenant-jwt"],
665
+ scope: ["shares.manage"],
666
+ params: AdminTenantParamsSchema,
667
+ body: { contentType: "application/json", schema: ShareGrantCreateRequestSchema },
668
+ responses: {
669
+ 200: { contentType: "application/json", schema: ShareGrantResponseSchema },
670
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema },
671
+ 403: { contentType: "application/json", schema: AdminErrorPayloadSchema },
672
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
673
+ },
674
+ notes: "The returned share id IS the public share token. Mounted only when the deployment can sign (HS256 mode) \u2014 exchange mints session JWTs, so grants exist only where minting does."
675
+ },
676
+ "shares.list": {
677
+ operationId: "shares.list",
678
+ title: "List shares",
679
+ summary: "List share grants in the tenant, newest first, cursor-paginated.",
680
+ method: "GET",
681
+ path: "/v1/tenants/:tenantId/shares",
682
+ credentials: ["api-token", "tenant-jwt"],
683
+ scope: ["shares.manage"],
684
+ params: AdminTenantParamsSchema,
685
+ query: ShareGrantListQuerySchema,
686
+ responses: {
687
+ 200: { contentType: "application/json", schema: ShareGrantListResponseSchema },
688
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
689
+ }
690
+ },
691
+ "shares.get": {
692
+ operationId: "shares.get",
693
+ title: "Get share",
694
+ summary: "Fetch one share grant.",
695
+ method: "GET",
696
+ path: "/v1/tenants/:tenantId/shares/:shareId",
697
+ credentials: ["api-token", "tenant-jwt"],
698
+ scope: ["shares.manage"],
699
+ params: AdminTenantShareParamsSchema,
700
+ responses: {
701
+ 200: { contentType: "application/json", schema: ShareGrantResponseSchema },
702
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
703
+ }
704
+ },
705
+ "shares.update": {
706
+ operationId: "shares.update",
707
+ title: "Update share",
708
+ summary: "Edit a grant in place \u2014 every embedded copy of the token follows the row.",
709
+ method: "PATCH",
710
+ path: "/v1/tenants/:tenantId/shares/:shareId",
711
+ credentials: ["api-token", "tenant-jwt"],
712
+ scope: ["shares.manage"],
713
+ params: AdminTenantShareParamsSchema,
714
+ body: { contentType: "application/json", schema: ShareGrantUpdateRequestSchema },
715
+ responses: {
716
+ 200: { contentType: "application/json", schema: ShareGrantResponseSchema },
717
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema },
718
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
719
+ }
720
+ },
721
+ "shares.delete": {
722
+ operationId: "shares.delete",
723
+ title: "Revoke share",
724
+ summary: "Delete a grant; exchange stops immediately, live sessions lapse at their exp.",
725
+ method: "DELETE",
726
+ path: "/v1/tenants/:tenantId/shares/:shareId",
727
+ credentials: ["api-token", "tenant-jwt"],
728
+ scope: ["shares.manage"],
729
+ params: AdminTenantShareParamsSchema,
730
+ responses: {
731
+ 204: {},
732
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
733
+ }
734
+ },
735
+ "shares.exchange": {
736
+ operationId: "shares.exchange",
737
+ title: "Exchange share token",
738
+ summary: "Trade a public share token for a short-lived document session JWT.",
739
+ method: "POST",
740
+ path: adminWirePaths.shareSessions,
741
+ credentials: [],
742
+ scope: [],
743
+ body: { contentType: "application/json", schema: ShareExchangeRequestSchema },
744
+ responses: {
745
+ 200: { contentType: "application/json", schema: ShareSessionResponseSchema },
746
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema },
747
+ 403: { contentType: "application/json", schema: AdminErrorPayloadSchema },
748
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema },
749
+ 410: { contentType: "application/json", schema: AdminErrorPayloadSchema },
750
+ 422: { contentType: "application/json", schema: AdminErrorPayloadSchema }
751
+ },
752
+ notes: "Unauthenticated, but requires a browser Origin header, checked against the grant allowlist. Unknown, revoked, and disabled tokens are indistinguishable (404). Passphrase-protected grants return 422 SharePasswordRequired until `password` is supplied. Mounted only when the deployment can sign (HS256 mode)."
452
753
  }
453
754
  };
454
755
  var DocIdParamsSchema = z.object({
@@ -477,6 +778,7 @@ var MutationResponseSchema = z.object({ meta: MutationMetaSchema }).passthrough(
477
778
  var docOperations = {
478
779
  "doc.head": {
479
780
  operationId: "doc.head",
781
+ title: "Open document",
480
782
  summary: "Open a document and return its head (versions, page count, security).",
481
783
  method: "GET",
482
784
  path: wireTemplates.docHead,
@@ -492,6 +794,7 @@ var docOperations = {
492
794
  },
493
795
  "doc.manifest": {
494
796
  operationId: "doc.manifest",
797
+ title: "Get manifest",
495
798
  summary: "Full layer manifest at the layer's current version.",
496
799
  method: "GET",
497
800
  path: wireTemplates.layerManifest,
@@ -507,6 +810,7 @@ var docOperations = {
507
810
  },
508
811
  "doc.metadata.get": {
509
812
  operationId: "doc.metadata.get",
813
+ title: "Get metadata",
510
814
  summary: "Document metadata (PDF info dictionary view) for a layer.",
511
815
  method: "GET",
512
816
  path: wireTemplates.layerMetadata,
@@ -522,6 +826,7 @@ var docOperations = {
522
826
  },
523
827
  "doc.render": {
524
828
  operationId: "doc.render",
829
+ title: "Render page",
525
830
  summary: "Render one page as an image at the current layer version.",
526
831
  method: "GET",
527
832
  path: wireTemplates.layerRenderPage,
@@ -538,6 +843,7 @@ var docOperations = {
538
843
  },
539
844
  "doc.text": {
540
845
  operationId: "doc.text",
846
+ title: "Extract page text",
541
847
  summary: "Extracted text content for one page.",
542
848
  method: "GET",
543
849
  path: wireTemplates.layerTextPage,
@@ -553,6 +859,7 @@ var docOperations = {
553
859
  },
554
860
  "doc.annotations.list": {
555
861
  operationId: "doc.annotations.list",
862
+ title: "List annotations",
556
863
  summary: "One page's annotations at the current layer version.",
557
864
  method: "GET",
558
865
  path: wireTemplates.layerAnnotationItems,
@@ -568,6 +875,7 @@ var docOperations = {
568
875
  },
569
876
  "doc.annotations.create": {
570
877
  operationId: "doc.annotations.create",
878
+ title: "Create annotation",
571
879
  summary: "Create an annotation on a page.",
572
880
  method: "POST",
573
881
  path: wireTemplates.layerAnnotationItems,
@@ -586,6 +894,7 @@ var docOperations = {
586
894
  },
587
895
  "doc.annotations.update": {
588
896
  operationId: "doc.annotations.update",
897
+ title: "Update annotation",
589
898
  summary: "Update one annotation by key.",
590
899
  method: "PATCH",
591
900
  path: wireTemplates.layerAnnotationItem,
@@ -603,6 +912,7 @@ var docOperations = {
603
912
  },
604
913
  "doc.annotations.delete": {
605
914
  operationId: "doc.annotations.delete",
915
+ title: "Delete annotation",
606
916
  summary: "Delete one annotation by key.",
607
917
  method: "DELETE",
608
918
  path: wireTemplates.layerAnnotationItem,
@@ -618,6 +928,7 @@ var docOperations = {
618
928
  },
619
929
  "doc.forms.get": {
620
930
  operationId: "doc.forms.get",
931
+ title: "Get form snapshot",
621
932
  summary: "Reconciled form snapshot: fields, widgets, values.",
622
933
  method: "GET",
623
934
  path: wireTemplates.layerForm,
@@ -633,6 +944,7 @@ var docOperations = {
633
944
  },
634
945
  "doc.forms.setValue": {
635
946
  operationId: "doc.forms.setValue",
947
+ title: "Set form value",
636
948
  summary: "Set one form field's value.",
637
949
  method: "POST",
638
950
  path: wireTemplates.layerFormFieldValue,
@@ -650,6 +962,7 @@ var docOperations = {
650
962
  },
651
963
  "doc.forms.reset": {
652
964
  operationId: "doc.forms.reset",
965
+ title: "Reset form field",
653
966
  summary: "Reset one form field to its default value.",
654
967
  method: "POST",
655
968
  path: wireTemplates.layerFormFieldReset,
@@ -665,6 +978,7 @@ var docOperations = {
665
978
  },
666
979
  "doc.forms.exportData": {
667
980
  operationId: "doc.forms.exportData",
981
+ title: "Export form data",
668
982
  summary: "Export form data as FDF or XFDF.",
669
983
  method: "GET",
670
984
  path: wireTemplates.layerFormData,
@@ -681,6 +995,7 @@ var docOperations = {
681
995
  },
682
996
  "doc.forms.importData": {
683
997
  operationId: "doc.forms.importData",
998
+ title: "Import form data",
684
999
  summary: "Import form data (FDF/XFDF), filling matching fields.",
685
1000
  method: "POST",
686
1001
  path: wireTemplates.layerFormData,
@@ -698,6 +1013,7 @@ var docOperations = {
698
1013
  },
699
1014
  "doc.pages.move": {
700
1015
  operationId: "doc.pages.move",
1016
+ title: "Move pages",
701
1017
  summary: "Reorder pages.",
702
1018
  method: "POST",
703
1019
  path: wireTemplates.layerPagesMove,
@@ -715,6 +1031,7 @@ var docOperations = {
715
1031
  },
716
1032
  "doc.pages.rotate": {
717
1033
  operationId: "doc.pages.rotate",
1034
+ title: "Rotate pages",
718
1035
  summary: "Set absolute rotation on pages.",
719
1036
  method: "POST",
720
1037
  path: wireTemplates.layerPagesRotate,
@@ -732,6 +1049,7 @@ var docOperations = {
732
1049
  },
733
1050
  "doc.pages.delete": {
734
1051
  operationId: "doc.pages.delete",
1052
+ title: "Delete pages",
735
1053
  summary: "Delete pages.",
736
1054
  method: "POST",
737
1055
  path: wireTemplates.layerPagesDelete,
@@ -749,6 +1067,7 @@ var docOperations = {
749
1067
  },
750
1068
  "doc.pages.flatten": {
751
1069
  operationId: "doc.pages.flatten",
1070
+ title: "Flatten pages",
752
1071
  summary: "Flatten annotations and form fields into page content.",
753
1072
  method: "POST",
754
1073
  path: wireTemplates.layerPagesFlatten,
@@ -766,6 +1085,7 @@ var docOperations = {
766
1085
  },
767
1086
  "doc.redactions.apply": {
768
1087
  operationId: "doc.redactions.apply",
1088
+ title: "Apply redactions",
769
1089
  summary: "Apply pending redactions, permanently removing content.",
770
1090
  method: "POST",
771
1091
  path: wireTemplates.layerRedactionsApply,
@@ -783,6 +1103,7 @@ var docOperations = {
783
1103
  },
784
1104
  "doc.download": {
785
1105
  operationId: "doc.download",
1106
+ title: "Download PDF",
786
1107
  summary: "Download the layer's current PDF (base plus layer edits).",
787
1108
  method: "GET",
788
1109
  path: wireTemplates.layerDownload,
@@ -798,6 +1119,19 @@ var docOperations = {
798
1119
  }
799
1120
  };
800
1121
  var allOperations = { ...adminOperations, ...docOperations };
1122
+ var docsGroups = {
1123
+ deployment: { title: "Deployment" },
1124
+ tenants: { title: "Tenants" },
1125
+ documents: { title: "Tenant documents" },
1126
+ tokens: { title: "Tokens" },
1127
+ shares: { title: "Shares" },
1128
+ doc: { title: "Document operations", slug: "document-operations" },
1129
+ "doc.annotations": { title: "Annotations" },
1130
+ "doc.forms": { title: "Forms" },
1131
+ "doc.metadata": { title: "Metadata" },
1132
+ "doc.pages": { title: "Pages" },
1133
+ "doc.redactions": { title: "Redactions" }
1134
+ };
801
1135
 
802
1136
  // src/openapi.ts
803
1137
  var STATUS_TEXT = {
@@ -808,6 +1142,7 @@ var STATUS_TEXT = {
808
1142
  403: "Forbidden",
809
1143
  404: "Not found",
810
1144
  409: "Conflict",
1145
+ 410: "Gone",
811
1146
  422: "Unprocessable"
812
1147
  };
813
1148
  var SECURITY_SCHEME = {
@@ -823,6 +1158,7 @@ function buildAdminOpenApiDocument(opts) {
823
1158
  registerSchema(schemas, "AdminErrorPayload", AdminErrorPayloadSchema);
824
1159
  registerSchema(schemas, "EngineErrorPayload", EngineErrorPayloadSchema2);
825
1160
  for (const op of Object.values(allOperations)) {
1161
+ assertDocsGroupsCover(op);
826
1162
  const openApiPath = toOpenApiPath(op.path);
827
1163
  const method = op.method.toLowerCase();
828
1164
  const entry = paths[openApiPath] ??= {};
@@ -837,23 +1173,29 @@ function buildAdminOpenApiDocument(opts) {
837
1173
  license: { name: "Apache-2.0", identifier: "Apache-2.0" }
838
1174
  },
839
1175
  paths: sortKeys(paths),
1176
+ // Docs navigation manifest: section titles and URL slugs for the API
1177
+ // reference, in sidebar order (see `docsGroups` in the registry).
1178
+ "x-docs-groups": docsGroups,
840
1179
  components: {
841
1180
  securitySchemes: {
842
1181
  apiToken: {
843
1182
  type: "http",
844
1183
  scheme: "bearer",
1184
+ "x-docs-title": "API token",
845
1185
  description: "The deployment's static root credential (CLOUDPDF_API_AUTH_TOKENS), valid on every surface."
846
1186
  },
847
1187
  tenantToken: {
848
1188
  type: "http",
849
1189
  scheme: "bearer",
850
1190
  bearerFormat: "JWT",
1191
+ "x-docs-title": "Tenant token",
851
1192
  description: "Delegated tenant JWT, valid only under its own /v1/tenants/{tenantId}/ subtree \u2014 the path tenant must equal the token's tenant_id. Doc-scoped viewer tokens are rejected on every admin route."
852
1193
  },
853
1194
  docToken: {
854
1195
  type: "http",
855
1196
  scheme: "bearer",
856
1197
  bearerFormat: "JWT",
1198
+ "x-docs-title": "Document token",
857
1199
  description: "Doc-scoped JWT, valid only on the /v1/docs/{docId} subtree it names, gated by the capability scopes it carries (each operation's x-required-capability)."
858
1200
  }
859
1201
  },
@@ -861,6 +1203,15 @@ function buildAdminOpenApiDocument(opts) {
861
1203
  }
862
1204
  };
863
1205
  }
1206
+ function assertDocsGroupsCover(op) {
1207
+ const { groups } = sdkOperationName(op.operationId);
1208
+ for (let depth = 1; depth <= groups.length; depth += 1) {
1209
+ const key = groups.slice(0, depth).join(".");
1210
+ if (!(key in docsGroups)) {
1211
+ throw new Error(`Operation ${op.operationId} has no docsGroups entry for "${key}"`);
1212
+ }
1213
+ }
1214
+ }
864
1215
  function operationObject(op, schemas) {
865
1216
  const parameters = [...pathParameters(op), ...queryParameters(op), ...headerParameters(op)];
866
1217
  const sdkName = sdkOperationName(op.operationId);
@@ -868,6 +1219,7 @@ function operationObject(op, schemas) {
868
1219
  operationId: op.operationId,
869
1220
  "x-fern-sdk-group-name": sdkName.groups,
870
1221
  "x-fern-sdk-method-name": sdkName.method,
1222
+ "x-docs-title": op.title,
871
1223
  summary: op.summary,
872
1224
  ...op.notes ? { description: op.notes } : {},
873
1225
  security: op.credentials.map((credential) => ({ [SECURITY_SCHEME[credential]]: [] })),