@cloudpdf/contract 3.0.0-next.1

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.
@@ -0,0 +1,1029 @@
1
+ // src/openapi.ts
2
+ import { zodToJsonSchema } from "zod-to-json-schema";
3
+ import { z as z2 } from "zod";
4
+ import { EngineErrorPayloadSchema as EngineErrorPayloadSchema2 } from "@embedpdf/engine-core/wire";
5
+
6
+ // src/index.ts
7
+ import { z } from "zod";
8
+ import {
9
+ AnnotationListPageSnapshotSchema,
10
+ DocumentHeadSchema,
11
+ DocumentManifestSchema,
12
+ DocumentMetadataSchema,
13
+ EngineErrorPayloadSchema,
14
+ FormSnapshotSchema,
15
+ MutationMetaSchema,
16
+ PageTextSnapshotSchema,
17
+ wireTemplates
18
+ } from "@embedpdf/engine-core/wire";
19
+ var sha256Hex = /^[0-9a-f]{64}$/i;
20
+ var docIdPattern = /^[A-Za-z0-9_-]+$/;
21
+ var tenantIdPattern = /^[A-Za-z0-9_-]+$/;
22
+ var adminWirePaths = {
23
+ tenants: "/v1/tenants",
24
+ tenant: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}`,
25
+ documents: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents`,
26
+ documentsInit: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/init`,
27
+ document: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}`,
28
+ documentCommit: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/commit`,
29
+ documentUploadDirect: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/upload-direct`,
30
+ documentDownload: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/download`,
31
+ /** The warmed dashboard-tile artifact. Returns 404 while `pending`/`locked`. */
32
+ documentThumbnail: (tenantId, docId) => `/v1/tenants/${encodeURIComponent(tenantId)}/documents/${encodeURIComponent(docId)}/thumbnail`,
33
+ tokenIssue: (tenantId) => `/v1/tenants/${encodeURIComponent(tenantId)}/tokens`,
34
+ tokenRevoke: (tenantId, jti) => `/v1/tenants/${encodeURIComponent(tenantId)}/tokens/${encodeURIComponent(jti)}/revoke`,
35
+ /** Deployment-global singletons: API-token only, no tenant context. */
36
+ deploymentLicenseStatus: "/v1/deployment/license/status"
37
+ };
38
+ var DedupModeSchema = z.enum(["always-create", "reuse-existing"]);
39
+ var DocumentStateSchema = z.enum(["pending", "ready", "failed", "deleting"]);
40
+ var AdminDocumentRecordSchema = z.object({
41
+ id: z.string(),
42
+ tenantId: z.string(),
43
+ state: DocumentStateSchema,
44
+ baseSha: z.string().nullable(),
45
+ storageSizeBytes: z.number().nullable(),
46
+ metadata: z.record(z.string(), z.unknown()).nullable(),
47
+ idempotencyKey: z.string().nullable(),
48
+ failureReason: z.string().nullable(),
49
+ /** Dashboard tile lifecycle. Optional because older servers omit these fields. */
50
+ thumbnailState: z.enum(["pending", "ready", "locked", "failed"]).optional(),
51
+ thumbnailUrl: z.string().nullable().optional(),
52
+ createdAt: z.number(),
53
+ updatedAt: z.number(),
54
+ createdBy: z.string().nullable()
55
+ });
56
+ var AdminDocumentInitRequestSchema = z.object({
57
+ contentLength: z.number().finite().min(1),
58
+ contentSha256: z.string().regex(sha256Hex),
59
+ metadata: z.record(z.string(), z.unknown()).optional(),
60
+ idempotencyKey: z.string().optional(),
61
+ dedupMode: DedupModeSchema.optional(),
62
+ docId: z.string().regex(docIdPattern).optional(),
63
+ uploadTtlSec: z.number().finite().min(60).max(3600).optional()
64
+ });
65
+ var AdminPresignedUploadSchema = z.object({
66
+ url: z.string(),
67
+ headers: z.record(z.string(), z.string()),
68
+ method: z.literal("PUT"),
69
+ expiresAt: z.number()
70
+ });
71
+ var AdminInitUploadSchema = z.discriminatedUnion("kind", [
72
+ z.object({
73
+ kind: z.literal("presigned"),
74
+ presigned: AdminPresignedUploadSchema,
75
+ key: z.string()
76
+ }),
77
+ z.object({
78
+ kind: z.literal("direct"),
79
+ url: z.string(),
80
+ key: z.string()
81
+ })
82
+ ]);
83
+ var AdminDocumentInitResponseSchema = z.discriminatedUnion("tag", [
84
+ z.object({
85
+ tag: z.literal("created"),
86
+ document: AdminDocumentRecordSchema,
87
+ upload: AdminInitUploadSchema
88
+ }),
89
+ z.object({
90
+ tag: z.literal("resumed"),
91
+ document: AdminDocumentRecordSchema,
92
+ upload: AdminInitUploadSchema
93
+ }),
94
+ z.object({
95
+ tag: z.literal("deduped"),
96
+ document: AdminDocumentRecordSchema
97
+ })
98
+ ]);
99
+ var AdminDocumentCommitRequestSchema = z.object({
100
+ sha256: z.string().regex(sha256Hex)
101
+ });
102
+ var AdminDocumentCommitResponseSchema = z.object({
103
+ document: AdminDocumentRecordSchema
104
+ });
105
+ var AdminDocumentResponseSchema = z.object({
106
+ document: AdminDocumentRecordSchema
107
+ });
108
+ var ADMIN_DOCUMENT_LIST_DEFAULT_LIMIT = 100;
109
+ var ADMIN_DOCUMENT_LIST_MAX_LIMIT = 200;
110
+ var AdminDocumentListQuerySchema = z.object({
111
+ limit: z.coerce.number().int().min(1).max(ADMIN_DOCUMENT_LIST_MAX_LIMIT).default(ADMIN_DOCUMENT_LIST_DEFAULT_LIMIT),
112
+ cursor: z.string().min(1).optional(),
113
+ state: DocumentStateSchema.optional()
114
+ });
115
+ var AdminDocumentListResponseSchema = z.object({
116
+ documents: z.array(AdminDocumentRecordSchema),
117
+ /**
118
+ * Opaque cursor for the next page; `null` on the last page. Optional
119
+ * because pre-pagination servers omit it — clients treat absence as
120
+ * "no continuation available", same convention as the thumbnail fields.
121
+ */
122
+ nextCursor: z.string().nullable().optional()
123
+ });
124
+ var AdminUploadDirectResponseSchema = z.object({
125
+ sha256: z.string().regex(sha256Hex)
126
+ });
127
+ var AdminErrorPayloadSchema = z.object({
128
+ error: z.object({
129
+ code: z.string(),
130
+ message: z.string()
131
+ })
132
+ });
133
+ var AdminThumbnailUnavailablePayloadSchema = z.object({
134
+ error: z.object({
135
+ code: z.string(),
136
+ message: z.string(),
137
+ state: z.string()
138
+ })
139
+ });
140
+ var AdminTenantParamsSchema = z.object({
141
+ tenantId: z.string().regex(tenantIdPattern)
142
+ });
143
+ var AdminTenantDocParamsSchema = z.object({
144
+ tenantId: z.string().regex(tenantIdPattern),
145
+ id: z.string().regex(docIdPattern)
146
+ });
147
+ var AdminTenantJtiParamsSchema = z.object({
148
+ tenantId: z.string().regex(tenantIdPattern),
149
+ jti: z.string().min(1).max(256)
150
+ });
151
+ var AdminTokenRevokeRequestSchema = z.object({
152
+ /** Optional human reason, written to the audit row. */
153
+ reason: z.string().max(1024).optional(),
154
+ /**
155
+ * The token's `exp` (unix seconds), used to GC the revocation row
156
+ * once the token would have expired anyway. Defaults server-side to
157
+ * now + 30 days.
158
+ */
159
+ expiresAtSeconds: z.number().int().positive().optional()
160
+ });
161
+ var AdminLicenseStatusResponseSchema = z.object({
162
+ license: z.unknown(),
163
+ reporting: z.unknown().nullable(),
164
+ usage: z.unknown().nullable()
165
+ });
166
+ var AdminTenantRecordSchema = z.object({
167
+ id: z.string(),
168
+ name: z.string(),
169
+ /** True when the namespace materialized on first use rather than via explicit create. */
170
+ autoProvisioned: z.boolean(),
171
+ createdAt: z.number()
172
+ });
173
+ var AdminTenantCreateRequestSchema = z.object({
174
+ id: z.string().regex(tenantIdPattern),
175
+ /** Display name; defaults to the id. */
176
+ name: z.string().min(1).max(256).optional()
177
+ });
178
+ var AdminTenantCreateResponseSchema = z.object({
179
+ tenant: AdminTenantRecordSchema,
180
+ /** False when the tenant already existed — create is ensure-style. */
181
+ created: z.boolean()
182
+ });
183
+ var AdminTenantResponseSchema = z.object({
184
+ tenant: AdminTenantRecordSchema
185
+ });
186
+ var AdminTenantListQuerySchema = z.object({
187
+ limit: z.coerce.number().int().min(1).max(ADMIN_DOCUMENT_LIST_MAX_LIMIT).default(ADMIN_DOCUMENT_LIST_DEFAULT_LIMIT),
188
+ cursor: z.string().min(1).optional()
189
+ });
190
+ var AdminTenantListResponseSchema = z.object({
191
+ tenants: z.array(AdminTenantRecordSchema),
192
+ /** Opaque cursor for the next page; `null` on the last page. */
193
+ nextCursor: z.string().nullable().optional()
194
+ });
195
+ var adminTenantScopes = [
196
+ "docs.create",
197
+ "docs.read",
198
+ "docs.delete",
199
+ "tokens.issue-doc",
200
+ "tokens.revoke"
201
+ ];
202
+ var AdminTokenIssueDocRequestSchema = z.object({
203
+ kind: z.literal("doc"),
204
+ /** Subject of the minted token — the end user's id in your system. */
205
+ sub: z.string().min(1).max(256),
206
+ docId: z.string().regex(docIdPattern),
207
+ layerName: z.string().min(1).max(256).optional(),
208
+ /**
209
+ * Doc capability scopes (`doc.open`, `doc.render`, …, plus the
210
+ * collab grammar). Validated server-side against the engine's scope
211
+ * vocabulary — an unknown string rejects the whole request.
212
+ */
213
+ scope: z.array(z.string().min(1).max(128)).min(1).max(64),
214
+ userId: z.string().max(256).optional(),
215
+ displayName: z.string().max(256).optional(),
216
+ groupId: z.string().max(256).optional(),
217
+ groups: z.array(z.string().max(256)).max(64).optional(),
218
+ /** Token lifetime in seconds. */
219
+ expiresIn: z.number().int().min(60).max(60 * 60 * 24 * 90)
220
+ });
221
+ var AdminTokenIssueTenantRequestSchema = z.object({
222
+ kind: z.literal("tenant"),
223
+ sub: z.string().min(1).max(256),
224
+ scope: z.array(z.union([z.literal("*"), z.enum(adminTenantScopes)])).min(1).max(16),
225
+ /** Token lifetime in seconds. */
226
+ expiresIn: z.number().int().min(60).max(60 * 60 * 24 * 90)
227
+ });
228
+ var AdminTokenIssueRequestSchema = z.discriminatedUnion("kind", [
229
+ AdminTokenIssueDocRequestSchema,
230
+ AdminTokenIssueTenantRequestSchema
231
+ ]);
232
+ var AdminTokenIssueResponseSchema = z.object({
233
+ token: z.string(),
234
+ jti: z.string(),
235
+ /** Unix seconds. */
236
+ expiresAt: z.number()
237
+ });
238
+ var adminOperations = {
239
+ "tenants.create": {
240
+ operationId: "tenants.create",
241
+ summary: "Create a tenant, or confirm it already exists \u2014 ensure-style, idempotent.",
242
+ method: "POST",
243
+ path: adminWirePaths.tenants,
244
+ credentials: ["api-token"],
245
+ scope: [],
246
+ body: { contentType: "application/json", schema: AdminTenantCreateRequestSchema },
247
+ responses: {
248
+ 200: { contentType: "application/json", schema: AdminTenantCreateResponseSchema },
249
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
250
+ }
251
+ },
252
+ "tenants.list": {
253
+ operationId: "tenants.list",
254
+ summary: "List tenants, newest first, cursor-paginated.",
255
+ method: "GET",
256
+ path: adminWirePaths.tenants,
257
+ credentials: ["api-token"],
258
+ scope: [],
259
+ query: AdminTenantListQuerySchema,
260
+ responses: {
261
+ 200: { contentType: "application/json", schema: AdminTenantListResponseSchema },
262
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
263
+ }
264
+ },
265
+ "tenants.get": {
266
+ operationId: "tenants.get",
267
+ summary: "Fetch one tenant record.",
268
+ method: "GET",
269
+ path: "/v1/tenants/:tenantId",
270
+ credentials: ["api-token"],
271
+ scope: [],
272
+ params: AdminTenantParamsSchema,
273
+ responses: {
274
+ 200: { contentType: "application/json", schema: AdminTenantResponseSchema },
275
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
276
+ }
277
+ },
278
+ "tenants.delete": {
279
+ operationId: "tenants.delete",
280
+ summary: "Delete a tenant and everything under it.",
281
+ method: "DELETE",
282
+ path: "/v1/tenants/:tenantId",
283
+ credentials: ["api-token"],
284
+ scope: [],
285
+ params: AdminTenantParamsSchema,
286
+ responses: {
287
+ 204: {},
288
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
289
+ },
290
+ notes: "Destroys the tenant and everything in its namespace \u2014 documents, layers, stored bytes, audit history. Irreversible."
291
+ },
292
+ "documents.init": {
293
+ operationId: "documents.init",
294
+ summary: "Begin an upload: create (or resume/dedupe) a pending document and issue upload access.",
295
+ method: "POST",
296
+ path: "/v1/tenants/:tenantId/documents/init",
297
+ credentials: ["api-token", "tenant-jwt"],
298
+ scope: ["docs.create"],
299
+ params: AdminTenantParamsSchema,
300
+ body: { contentType: "application/json", schema: AdminDocumentInitRequestSchema },
301
+ responses: {
302
+ 200: { contentType: "application/json", schema: AdminDocumentInitResponseSchema },
303
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
304
+ }
305
+ },
306
+ "documents.commit": {
307
+ operationId: "documents.commit",
308
+ summary: "Finish an upload: verify the SHA-256 and promote the document to ready.",
309
+ method: "POST",
310
+ path: "/v1/tenants/:tenantId/documents/:id/commit",
311
+ credentials: ["api-token", "tenant-jwt"],
312
+ scope: ["docs.create"],
313
+ params: AdminTenantDocParamsSchema,
314
+ body: { contentType: "application/json", schema: AdminDocumentCommitRequestSchema },
315
+ responses: {
316
+ 200: { contentType: "application/json", schema: AdminDocumentCommitResponseSchema },
317
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema },
318
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
319
+ }
320
+ },
321
+ "documents.uploadDirect": {
322
+ operationId: "documents.uploadDirect",
323
+ summary: "Upload the PDF bytes through the origin instead of a presigned URL.",
324
+ method: "POST",
325
+ path: "/v1/tenants/:tenantId/documents/:id/upload-direct",
326
+ credentials: ["api-token", "tenant-jwt"],
327
+ scope: ["docs.create"],
328
+ params: AdminTenantDocParamsSchema,
329
+ body: {
330
+ contentType: ["application/pdf", "application/octet-stream", "multipart/form-data"]
331
+ },
332
+ responses: {
333
+ 200: { contentType: "application/json", schema: AdminUploadDirectResponseSchema },
334
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
335
+ }
336
+ },
337
+ "documents.list": {
338
+ operationId: "documents.list",
339
+ summary: "List documents in the tenant, newest first, cursor-paginated.",
340
+ method: "GET",
341
+ path: "/v1/tenants/:tenantId/documents",
342
+ credentials: ["api-token", "tenant-jwt"],
343
+ scope: ["docs.read"],
344
+ params: AdminTenantParamsSchema,
345
+ query: AdminDocumentListQuerySchema,
346
+ responses: {
347
+ 200: { contentType: "application/json", schema: AdminDocumentListResponseSchema },
348
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
349
+ }
350
+ },
351
+ "documents.get": {
352
+ operationId: "documents.get",
353
+ summary: "Fetch one document record.",
354
+ method: "GET",
355
+ path: "/v1/tenants/:tenantId/documents/:id",
356
+ credentials: ["api-token", "tenant-jwt"],
357
+ scope: ["docs.read"],
358
+ params: AdminTenantDocParamsSchema,
359
+ responses: {
360
+ 200: { contentType: "application/json", schema: AdminDocumentResponseSchema },
361
+ 403: { contentType: "application/json", schema: AdminErrorPayloadSchema },
362
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
363
+ }
364
+ },
365
+ "documents.download": {
366
+ operationId: "documents.download",
367
+ summary: "Download the stored base PDF bytes.",
368
+ method: "GET",
369
+ path: "/v1/tenants/:tenantId/documents/:id/download",
370
+ credentials: ["api-token", "tenant-jwt"],
371
+ scope: ["docs.read"],
372
+ params: AdminTenantDocParamsSchema,
373
+ responses: {
374
+ 200: { contentType: "application/pdf" },
375
+ 403: { contentType: "application/json", schema: AdminErrorPayloadSchema },
376
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
377
+ }
378
+ },
379
+ "documents.delete": {
380
+ operationId: "documents.delete",
381
+ summary: "Delete a document and its stored artifacts.",
382
+ method: "DELETE",
383
+ path: "/v1/tenants/:tenantId/documents/:id",
384
+ credentials: ["api-token", "tenant-jwt"],
385
+ scope: ["docs.delete"],
386
+ params: AdminTenantDocParamsSchema,
387
+ responses: {
388
+ 204: {},
389
+ 403: { contentType: "application/json", schema: AdminErrorPayloadSchema },
390
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
391
+ }
392
+ },
393
+ "documents.thumbnail": {
394
+ operationId: "documents.thumbnail",
395
+ summary: "Fetch the warmed dashboard-tile render for a document.",
396
+ method: "GET",
397
+ path: "/v1/tenants/:tenantId/documents/:id/thumbnail",
398
+ credentials: ["api-token", "tenant-jwt"],
399
+ scope: ["docs.read"],
400
+ params: AdminTenantDocParamsSchema,
401
+ responses: {
402
+ 200: { contentType: ["image/webp", "image/png"] },
403
+ 404: { contentType: "application/json", schema: AdminThumbnailUnavailablePayloadSchema }
404
+ }
405
+ },
406
+ "deployment.licenseStatus": {
407
+ operationId: "deployment.licenseStatus",
408
+ summary: "License decision plus usage-reporting and meter snapshots for this deployment.",
409
+ method: "GET",
410
+ path: adminWirePaths.deploymentLicenseStatus,
411
+ credentials: ["api-token"],
412
+ scope: [],
413
+ responses: {
414
+ 200: { contentType: "application/json", schema: AdminLicenseStatusResponseSchema }
415
+ }
416
+ },
417
+ "tokens.issue": {
418
+ operationId: "tokens.issue",
419
+ summary: "Mint a delegated JWT: a doc token, or (API token only) a tenant token.",
420
+ method: "POST",
421
+ path: "/v1/tenants/:tenantId/tokens",
422
+ credentials: ["api-token", "tenant-jwt"],
423
+ scope: ["tokens.issue-doc"],
424
+ params: AdminTenantParamsSchema,
425
+ body: { contentType: "application/json", schema: AdminTokenIssueRequestSchema },
426
+ responses: {
427
+ 200: { contentType: "application/json", schema: AdminTokenIssueResponseSchema },
428
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema },
429
+ 403: { contentType: "application/json", schema: AdminErrorPayloadSchema },
430
+ 404: { contentType: "application/json", schema: AdminErrorPayloadSchema }
431
+ },
432
+ notes: 'kind "tenant" requires the API token \u2014 authority mints only downward. Mounted only when the deployment can sign (HS256 mode); asymmetric deployments mint with their own private key.'
433
+ },
434
+ "tokens.revoke": {
435
+ operationId: "tokens.revoke",
436
+ summary: "Revoke a token by jti; live sessions drop on their next heartbeat.",
437
+ method: "POST",
438
+ path: "/v1/tenants/:tenantId/tokens/:jti/revoke",
439
+ credentials: ["api-token", "tenant-jwt"],
440
+ scope: ["tokens.revoke"],
441
+ params: AdminTenantJtiParamsSchema,
442
+ body: {
443
+ contentType: "application/json",
444
+ schema: AdminTokenRevokeRequestSchema,
445
+ required: false
446
+ },
447
+ responses: {
448
+ 204: {},
449
+ 400: { contentType: "application/json", schema: AdminErrorPayloadSchema }
450
+ },
451
+ notes: "Mounted only when the deployment enables token revocation."
452
+ }
453
+ };
454
+ var DocIdParamsSchema = z.object({
455
+ docId: z.string().regex(docIdPattern)
456
+ });
457
+ var DocLayerParamsSchema = DocIdParamsSchema.extend({
458
+ layerName: z.string().min(1)
459
+ });
460
+ var DocPageParamsSchema = DocLayerParamsSchema.extend({
461
+ pon: z.coerce.number().int().min(0)
462
+ });
463
+ var DocAnnotationParamsSchema = DocPageParamsSchema.extend({
464
+ annotKey: z.string().min(1)
465
+ });
466
+ var DocFieldParamsSchema = DocLayerParamsSchema.extend({
467
+ fieldKey: z.string().min(1)
468
+ });
469
+ var documentPasswordHeader = {
470
+ name: "X-Document-Password",
471
+ required: false,
472
+ description: "Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead."
473
+ };
474
+ var looseJson = z.record(z.string(), z.unknown());
475
+ var docCredentials = ["api-token", "doc-jwt"];
476
+ var MutationResponseSchema = z.object({ meta: MutationMetaSchema }).passthrough();
477
+ var docOperations = {
478
+ "doc.head": {
479
+ operationId: "doc.head",
480
+ summary: "Open a document and return its head (versions, page count, security).",
481
+ method: "GET",
482
+ path: wireTemplates.docHead,
483
+ credentials: docCredentials,
484
+ scope: [],
485
+ docCapabilities: ["doc.open"],
486
+ requestHeaders: [documentPasswordHeader],
487
+ params: DocIdParamsSchema,
488
+ responses: {
489
+ 200: { contentType: "application/json", schema: DocumentHeadSchema },
490
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
491
+ }
492
+ },
493
+ "doc.manifest": {
494
+ operationId: "doc.manifest",
495
+ summary: "Full layer manifest at the layer's current version.",
496
+ method: "GET",
497
+ path: wireTemplates.layerManifest,
498
+ credentials: docCredentials,
499
+ scope: [],
500
+ docCapabilities: ["doc.open"],
501
+ requestHeaders: [documentPasswordHeader],
502
+ params: DocLayerParamsSchema,
503
+ responses: {
504
+ 200: { contentType: "application/json", schema: DocumentManifestSchema },
505
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
506
+ }
507
+ },
508
+ "doc.metadata.get": {
509
+ operationId: "doc.metadata.get",
510
+ summary: "Document metadata (PDF info dictionary view) for a layer.",
511
+ method: "GET",
512
+ path: wireTemplates.layerMetadata,
513
+ credentials: docCredentials,
514
+ scope: [],
515
+ docCapabilities: ["doc.open"],
516
+ requestHeaders: [documentPasswordHeader],
517
+ params: DocLayerParamsSchema,
518
+ responses: {
519
+ 200: { contentType: "application/json", schema: DocumentMetadataSchema },
520
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
521
+ }
522
+ },
523
+ "doc.render": {
524
+ operationId: "doc.render",
525
+ summary: "Render one page as an image at the current layer version.",
526
+ method: "GET",
527
+ path: wireTemplates.layerRenderPage,
528
+ credentials: docCredentials,
529
+ scope: [],
530
+ docCapabilities: ["doc.render"],
531
+ requestHeaders: [documentPasswordHeader],
532
+ params: DocPageParamsSchema,
533
+ notes: "Render parameters (viewport, format) pass as flat dotted query keys, e.g. `?viewport.kind=width&viewport.width=800`; the full grammar is documented with the viewer.",
534
+ responses: {
535
+ 200: { contentType: "image/webp" },
536
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
537
+ }
538
+ },
539
+ "doc.text": {
540
+ operationId: "doc.text",
541
+ summary: "Extracted text content for one page.",
542
+ method: "GET",
543
+ path: wireTemplates.layerTextPage,
544
+ credentials: docCredentials,
545
+ scope: [],
546
+ docCapabilities: ["doc.text.copy"],
547
+ requestHeaders: [documentPasswordHeader],
548
+ params: DocPageParamsSchema,
549
+ responses: {
550
+ 200: { contentType: "application/json", schema: PageTextSnapshotSchema },
551
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
552
+ }
553
+ },
554
+ "doc.annotations.list": {
555
+ operationId: "doc.annotations.list",
556
+ summary: "One page's annotations at the current layer version.",
557
+ method: "GET",
558
+ path: wireTemplates.layerAnnotationItems,
559
+ credentials: docCredentials,
560
+ scope: [],
561
+ docCapabilities: ["doc.annotate.read"],
562
+ requestHeaders: [documentPasswordHeader],
563
+ params: DocPageParamsSchema,
564
+ responses: {
565
+ 200: { contentType: "application/json", schema: AnnotationListPageSnapshotSchema },
566
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
567
+ }
568
+ },
569
+ "doc.annotations.create": {
570
+ operationId: "doc.annotations.create",
571
+ summary: "Create an annotation on a page.",
572
+ method: "POST",
573
+ path: wireTemplates.layerAnnotationItems,
574
+ credentials: docCredentials,
575
+ scope: [],
576
+ docCapabilities: ["doc.annotate.modify"],
577
+ requestHeaders: [documentPasswordHeader],
578
+ params: DocPageParamsSchema,
579
+ body: { contentType: "application/json", schema: looseJson },
580
+ notes: "Doc JWTs may instead carry collab scopes (annotations:create:self, \u2026) that refine per-annotation authorship rules; the API token is exempt from both.",
581
+ responses: {
582
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
583
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
584
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
585
+ }
586
+ },
587
+ "doc.annotations.update": {
588
+ operationId: "doc.annotations.update",
589
+ summary: "Update one annotation by key.",
590
+ method: "PATCH",
591
+ path: wireTemplates.layerAnnotationItem,
592
+ credentials: docCredentials,
593
+ scope: [],
594
+ docCapabilities: ["doc.annotate.modify"],
595
+ requestHeaders: [documentPasswordHeader],
596
+ params: DocAnnotationParamsSchema,
597
+ body: { contentType: "application/json", schema: looseJson },
598
+ responses: {
599
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
600
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
601
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
602
+ }
603
+ },
604
+ "doc.annotations.delete": {
605
+ operationId: "doc.annotations.delete",
606
+ summary: "Delete one annotation by key.",
607
+ method: "DELETE",
608
+ path: wireTemplates.layerAnnotationItem,
609
+ credentials: docCredentials,
610
+ scope: [],
611
+ docCapabilities: ["doc.annotate.modify"],
612
+ requestHeaders: [documentPasswordHeader],
613
+ params: DocAnnotationParamsSchema,
614
+ responses: {
615
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
616
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
617
+ }
618
+ },
619
+ "doc.forms.get": {
620
+ operationId: "doc.forms.get",
621
+ summary: "Reconciled form snapshot: fields, widgets, values.",
622
+ method: "GET",
623
+ path: wireTemplates.layerForm,
624
+ credentials: docCredentials,
625
+ scope: [],
626
+ docCapabilities: ["doc.forms.read"],
627
+ requestHeaders: [documentPasswordHeader],
628
+ params: DocLayerParamsSchema,
629
+ responses: {
630
+ 200: { contentType: "application/json", schema: FormSnapshotSchema },
631
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
632
+ }
633
+ },
634
+ "doc.forms.setValue": {
635
+ operationId: "doc.forms.setValue",
636
+ summary: "Set one form field's value.",
637
+ method: "POST",
638
+ path: wireTemplates.layerFormFieldValue,
639
+ credentials: docCredentials,
640
+ scope: [],
641
+ docCapabilities: ["doc.forms.fill"],
642
+ requestHeaders: [documentPasswordHeader],
643
+ params: DocFieldParamsSchema,
644
+ body: { contentType: "application/json", schema: looseJson },
645
+ responses: {
646
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
647
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
648
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
649
+ }
650
+ },
651
+ "doc.forms.reset": {
652
+ operationId: "doc.forms.reset",
653
+ summary: "Reset one form field to its default value.",
654
+ method: "POST",
655
+ path: wireTemplates.layerFormFieldReset,
656
+ credentials: docCredentials,
657
+ scope: [],
658
+ docCapabilities: ["doc.forms.fill"],
659
+ requestHeaders: [documentPasswordHeader],
660
+ params: DocFieldParamsSchema,
661
+ responses: {
662
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
663
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
664
+ }
665
+ },
666
+ "doc.forms.exportData": {
667
+ operationId: "doc.forms.exportData",
668
+ summary: "Export form data as FDF or XFDF.",
669
+ method: "GET",
670
+ path: wireTemplates.layerFormData,
671
+ credentials: docCredentials,
672
+ scope: [],
673
+ docCapabilities: ["doc.forms.read"],
674
+ requestHeaders: [documentPasswordHeader],
675
+ params: DocLayerParamsSchema,
676
+ query: z.object({ format: z.enum(["fdf", "xfdf"]).optional() }),
677
+ responses: {
678
+ 200: { contentType: ["application/vnd.adobe.xfdf", "application/vnd.fdf"] },
679
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
680
+ }
681
+ },
682
+ "doc.forms.importData": {
683
+ operationId: "doc.forms.importData",
684
+ summary: "Import form data (FDF/XFDF), filling matching fields.",
685
+ method: "POST",
686
+ path: wireTemplates.layerFormData,
687
+ credentials: docCredentials,
688
+ scope: [],
689
+ docCapabilities: ["doc.forms.fill"],
690
+ requestHeaders: [documentPasswordHeader],
691
+ params: DocLayerParamsSchema,
692
+ body: { contentType: "application/json", schema: looseJson },
693
+ responses: {
694
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
695
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
696
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
697
+ }
698
+ },
699
+ "doc.pages.move": {
700
+ operationId: "doc.pages.move",
701
+ summary: "Reorder pages.",
702
+ method: "POST",
703
+ path: wireTemplates.layerPagesMove,
704
+ credentials: docCredentials,
705
+ scope: [],
706
+ docCapabilities: ["doc.pages.assemble"],
707
+ requestHeaders: [documentPasswordHeader],
708
+ params: DocLayerParamsSchema,
709
+ body: { contentType: "application/json", schema: looseJson },
710
+ responses: {
711
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
712
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
713
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
714
+ }
715
+ },
716
+ "doc.pages.rotate": {
717
+ operationId: "doc.pages.rotate",
718
+ summary: "Set absolute rotation on pages.",
719
+ method: "POST",
720
+ path: wireTemplates.layerPagesRotate,
721
+ credentials: docCredentials,
722
+ scope: [],
723
+ docCapabilities: ["doc.pages.assemble"],
724
+ requestHeaders: [documentPasswordHeader],
725
+ params: DocLayerParamsSchema,
726
+ body: { contentType: "application/json", schema: looseJson },
727
+ responses: {
728
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
729
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
730
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
731
+ }
732
+ },
733
+ "doc.pages.delete": {
734
+ operationId: "doc.pages.delete",
735
+ summary: "Delete pages.",
736
+ method: "POST",
737
+ path: wireTemplates.layerPagesDelete,
738
+ credentials: docCredentials,
739
+ scope: [],
740
+ docCapabilities: ["doc.pages.assemble"],
741
+ requestHeaders: [documentPasswordHeader],
742
+ params: DocLayerParamsSchema,
743
+ body: { contentType: "application/json", schema: looseJson },
744
+ responses: {
745
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
746
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
747
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
748
+ }
749
+ },
750
+ "doc.pages.flatten": {
751
+ operationId: "doc.pages.flatten",
752
+ summary: "Flatten annotations and form fields into page content.",
753
+ method: "POST",
754
+ path: wireTemplates.layerPagesFlatten,
755
+ credentials: docCredentials,
756
+ scope: [],
757
+ docCapabilities: ["doc.pages.modify", "doc.annotate.modify"],
758
+ requestHeaders: [documentPasswordHeader],
759
+ params: DocLayerParamsSchema,
760
+ body: { contentType: "application/json", schema: looseJson, required: false },
761
+ responses: {
762
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
763
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
764
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
765
+ }
766
+ },
767
+ "doc.redactions.apply": {
768
+ operationId: "doc.redactions.apply",
769
+ summary: "Apply pending redactions, permanently removing content.",
770
+ method: "POST",
771
+ path: wireTemplates.layerRedactionsApply,
772
+ credentials: docCredentials,
773
+ scope: [],
774
+ docCapabilities: ["doc.pages.modify", "doc.annotate.modify", "doc.redact"],
775
+ requestHeaders: [documentPasswordHeader],
776
+ params: DocLayerParamsSchema,
777
+ body: { contentType: "application/json", schema: looseJson, required: false },
778
+ responses: {
779
+ 200: { contentType: "application/json", schema: MutationResponseSchema },
780
+ 400: { contentType: "application/json", schema: EngineErrorPayloadSchema },
781
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
782
+ }
783
+ },
784
+ "doc.download": {
785
+ operationId: "doc.download",
786
+ summary: "Download the layer's current PDF (base plus layer edits).",
787
+ method: "GET",
788
+ path: wireTemplates.layerDownload,
789
+ credentials: docCredentials,
790
+ scope: [],
791
+ docCapabilities: ["doc.download"],
792
+ requestHeaders: [documentPasswordHeader],
793
+ params: DocLayerParamsSchema,
794
+ responses: {
795
+ 200: { contentType: "application/pdf" },
796
+ 404: { contentType: "application/json", schema: EngineErrorPayloadSchema }
797
+ }
798
+ }
799
+ };
800
+ var allOperations = { ...adminOperations, ...docOperations };
801
+
802
+ // src/openapi.ts
803
+ var STATUS_TEXT = {
804
+ 200: "OK",
805
+ 204: "No content",
806
+ 400: "Bad request",
807
+ 401: "Unauthorized",
808
+ 403: "Forbidden",
809
+ 404: "Not found",
810
+ 409: "Conflict",
811
+ 422: "Unprocessable"
812
+ };
813
+ var SECURITY_SCHEME = {
814
+ "api-token": "apiToken",
815
+ "tenant-jwt": "tenantToken",
816
+ "doc-jwt": "docToken"
817
+ };
818
+ var ADMIN_ERROR_REF = "#/components/schemas/AdminErrorPayload";
819
+ var ENGINE_ERROR_REF = "#/components/schemas/EngineErrorPayload";
820
+ function buildAdminOpenApiDocument(opts) {
821
+ const paths = {};
822
+ const schemas = {};
823
+ registerSchema(schemas, "AdminErrorPayload", AdminErrorPayloadSchema);
824
+ registerSchema(schemas, "EngineErrorPayload", EngineErrorPayloadSchema2);
825
+ for (const op of Object.values(allOperations)) {
826
+ const openApiPath = toOpenApiPath(op.path);
827
+ const method = op.method.toLowerCase();
828
+ const entry = paths[openApiPath] ??= {};
829
+ entry[method] = operationObject(op, schemas);
830
+ }
831
+ return {
832
+ openapi: "3.1.0",
833
+ info: {
834
+ title: "CloudPDF Engine API",
835
+ version: opts.version,
836
+ description: "The backend-callable surface of the CloudPDF document engine: deployment and tenant administration plus the document operations (reads, annotations, forms, page operations, redaction, download). Interactive viewing goes through the CloudPDF viewer SDKs, whose session protocol is deliberately not part of this API. Generated from the @cloudpdf/contract operation registry \u2014 do not edit by hand.",
837
+ license: { name: "Apache-2.0", identifier: "Apache-2.0" }
838
+ },
839
+ paths: sortKeys(paths),
840
+ components: {
841
+ securitySchemes: {
842
+ apiToken: {
843
+ type: "http",
844
+ scheme: "bearer",
845
+ description: "The deployment's static root credential (CLOUDPDF_API_AUTH_TOKENS), valid on every surface."
846
+ },
847
+ tenantToken: {
848
+ type: "http",
849
+ scheme: "bearer",
850
+ bearerFormat: "JWT",
851
+ 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
+ },
853
+ docToken: {
854
+ type: "http",
855
+ scheme: "bearer",
856
+ bearerFormat: "JWT",
857
+ 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
+ }
859
+ },
860
+ schemas
861
+ }
862
+ };
863
+ }
864
+ function operationObject(op, schemas) {
865
+ const parameters = [...pathParameters(op), ...queryParameters(op), ...headerParameters(op)];
866
+ const sdkName = sdkOperationName(op.operationId);
867
+ const out = {
868
+ operationId: op.operationId,
869
+ "x-fern-sdk-group-name": sdkName.groups,
870
+ "x-fern-sdk-method-name": sdkName.method,
871
+ summary: op.summary,
872
+ ...op.notes ? { description: op.notes } : {},
873
+ security: op.credentials.map((credential) => ({ [SECURITY_SCHEME[credential]]: [] })),
874
+ "x-required-scope": [...op.scope],
875
+ ...op.docCapabilities ? { "x-required-capability": [...op.docCapabilities] } : {},
876
+ ...parameters.length > 0 ? { parameters } : {},
877
+ ...op.body ? { requestBody: requestBody(op, op.body, schemas) } : {},
878
+ responses: responses(op, schemas)
879
+ };
880
+ return out;
881
+ }
882
+ function sdkOperationName(operationId) {
883
+ const parts = operationId.split(".");
884
+ const method = parts.pop();
885
+ if (!method || parts.length === 0) {
886
+ throw new Error(`Operation ID must use resource.method form: ${operationId}`);
887
+ }
888
+ return { groups: parts, method };
889
+ }
890
+ function headerParameters(op) {
891
+ if (!op.requestHeaders) return [];
892
+ return op.requestHeaders.map((header) => ({
893
+ name: header.name,
894
+ in: "header",
895
+ required: header.required ?? false,
896
+ description: header.description,
897
+ schema: { type: "string" }
898
+ }));
899
+ }
900
+ function pathParameters(op) {
901
+ const names = [...op.path.matchAll(/:([A-Za-z0-9_]+)/g)].map((m) => m[1]);
902
+ const shape = op.params ? objectShape(op.params) : {};
903
+ return names.map((name) => ({
904
+ name,
905
+ in: "path",
906
+ required: true,
907
+ schema: shape[name] ? inlineSchemaOf(shape[name]) : { type: "string" }
908
+ }));
909
+ }
910
+ function queryParameters(op) {
911
+ if (!op.query) return [];
912
+ return Object.entries(objectShape(op.query)).map(([name, prop]) => ({
913
+ name,
914
+ in: "query",
915
+ required: !prop.isOptional(),
916
+ // Optionality is carried by `required` above; strip the ZodOptional
917
+ // wrapper so the schema describes the value's shape rather than
918
+ // `T | undefined` (which converts to a meaningless anyOf/not).
919
+ schema: inlineSchemaOf(unwrapOptional(prop))
920
+ }));
921
+ }
922
+ function unwrapOptional(schema) {
923
+ let current = schema;
924
+ while (current instanceof z2.ZodOptional) current = current.unwrap();
925
+ return current;
926
+ }
927
+ function requestBody(op, body, schemas) {
928
+ const schema = body.schema ? registerSchema(schemas, `${sdkTypeName(op.operationId)}Request`, body.schema) : void 0;
929
+ return {
930
+ required: body.required ?? true,
931
+ content: contentObject(body.contentType, schema)
932
+ };
933
+ }
934
+ function responses(op, schemas) {
935
+ const out = {};
936
+ const typedResponses = Object.values(op.responses).filter((response) => response.schema).length;
937
+ for (const [status, response] of Object.entries(op.responses)) {
938
+ const schema = response.schema ? registerSchema(
939
+ schemas,
940
+ `${sdkTypeName(op.operationId)}${typedResponses > 1 ? status : ""}Response`,
941
+ response.schema
942
+ ) : void 0;
943
+ out[status] = {
944
+ description: STATUS_TEXT[Number(status)] ?? "Response",
945
+ ...response.contentType ? { content: contentObject(response.contentType, schema) } : {}
946
+ };
947
+ }
948
+ out["default"] = {
949
+ description: "Error",
950
+ content: {
951
+ "application/json": {
952
+ schema: { $ref: op.credentials.includes("doc-jwt") ? ENGINE_ERROR_REF : ADMIN_ERROR_REF }
953
+ }
954
+ }
955
+ };
956
+ return out;
957
+ }
958
+ function contentObject(contentType, schema) {
959
+ const types = typeof contentType === "string" ? [contentType] : contentType;
960
+ return Object.fromEntries(types.map((t) => [t, mediaObject(t, schema)]));
961
+ }
962
+ function mediaObject(contentType, schema) {
963
+ if (schema) return { schema };
964
+ if (contentType === "multipart/form-data") {
965
+ return {
966
+ schema: {
967
+ type: "object",
968
+ properties: { file: { type: "string", format: "binary" } },
969
+ required: ["file"]
970
+ }
971
+ };
972
+ }
973
+ return { schema: { type: "string", format: "binary" } };
974
+ }
975
+ function registerSchema(schemas, name, schema) {
976
+ if (schemas[name]) throw new Error(`Duplicate OpenAPI schema name: ${name}`);
977
+ const componentPath = ["components", "schemas", name];
978
+ const referenced = zodToJsonSchema(schema, {
979
+ target: "openApi3",
980
+ $refStrategy: "root",
981
+ basePath: ["#", ...componentPath]
982
+ });
983
+ schemas[name] = breakAncestorReferences(referenced, componentPath);
984
+ return { $ref: `#/components/schemas/${name}` };
985
+ }
986
+ function breakAncestorReferences(value, path) {
987
+ if (Array.isArray(value)) {
988
+ return value.map((child, index) => breakAncestorReferences(child, [...path, String(index)]));
989
+ }
990
+ if (!value || typeof value !== "object") return value;
991
+ const record = value;
992
+ if (typeof record.$ref === "string" && record.$ref.startsWith("#/")) {
993
+ const target = record.$ref.slice(2).split("/").map((segment) => segment.replaceAll("~1", "/").replaceAll("~0", "~"));
994
+ if (target.length <= path.length && target.every((segment, index) => path[index] === segment)) {
995
+ return {};
996
+ }
997
+ }
998
+ return Object.fromEntries(
999
+ Object.entries(record).map(([key, child]) => [
1000
+ key,
1001
+ breakAncestorReferences(child, [...path, key])
1002
+ ])
1003
+ );
1004
+ }
1005
+ function inlineSchemaOf(schema) {
1006
+ return zodToJsonSchema(schema, {
1007
+ target: "openApi3",
1008
+ $refStrategy: "none"
1009
+ });
1010
+ }
1011
+ function sdkTypeName(operationId) {
1012
+ return operationId.split(".").map((part) => `${part[0].toUpperCase()}${part.slice(1)}`).join("");
1013
+ }
1014
+ function objectShape(schema) {
1015
+ if (schema instanceof z2.ZodObject) {
1016
+ return schema.shape;
1017
+ }
1018
+ return {};
1019
+ }
1020
+ function toOpenApiPath(template) {
1021
+ return template.replace(/:([A-Za-z0-9_]+)/g, "{$1}");
1022
+ }
1023
+ function sortKeys(obj) {
1024
+ return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)));
1025
+ }
1026
+ export {
1027
+ buildAdminOpenApiDocument
1028
+ };
1029
+ //# sourceMappingURL=openapi.js.map