@contractspec/lib.files 1.56.1 → 1.58.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 (44) hide show
  1. package/dist/contracts/index.d.ts +1080 -1086
  2. package/dist/contracts/index.d.ts.map +1 -1
  3. package/dist/contracts/index.js +575 -854
  4. package/dist/docs/files.docblock.d.ts +2 -1
  5. package/dist/docs/files.docblock.d.ts.map +1 -0
  6. package/dist/docs/files.docblock.js +17 -22
  7. package/dist/docs/index.d.ts +2 -1
  8. package/dist/docs/index.d.ts.map +1 -0
  9. package/dist/docs/index.js +66 -1
  10. package/dist/entities/index.d.ts +134 -139
  11. package/dist/entities/index.d.ts.map +1 -1
  12. package/dist/entities/index.js +228 -257
  13. package/dist/events.d.ts +357 -363
  14. package/dist/events.d.ts.map +1 -1
  15. package/dist/events.js +217 -400
  16. package/dist/files.capability.d.ts +2 -7
  17. package/dist/files.capability.d.ts.map +1 -1
  18. package/dist/files.capability.js +29 -25
  19. package/dist/files.feature.d.ts +1 -7
  20. package/dist/files.feature.d.ts.map +1 -1
  21. package/dist/files.feature.js +50 -131
  22. package/dist/index.d.ts +7 -6
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +1411 -8
  25. package/dist/node/contracts/index.js +576 -0
  26. package/dist/node/docs/files.docblock.js +65 -0
  27. package/dist/node/docs/index.js +65 -0
  28. package/dist/node/entities/index.js +235 -0
  29. package/dist/node/events.js +219 -0
  30. package/dist/node/files.capability.js +28 -0
  31. package/dist/node/files.feature.js +51 -0
  32. package/dist/node/index.js +1410 -0
  33. package/dist/node/storage/index.js +268 -0
  34. package/dist/storage/index.d.ts +163 -166
  35. package/dist/storage/index.d.ts.map +1 -1
  36. package/dist/storage/index.js +266 -266
  37. package/package.json +104 -30
  38. package/dist/contracts/index.js.map +0 -1
  39. package/dist/docs/files.docblock.js.map +0 -1
  40. package/dist/entities/index.js.map +0 -1
  41. package/dist/events.js.map +0 -1
  42. package/dist/files.capability.js.map +0 -1
  43. package/dist/files.feature.js.map +0 -1
  44. package/dist/storage/index.js.map +0 -1
@@ -0,0 +1,576 @@
1
+ // src/contracts/index.ts
2
+ import { ScalarTypeEnum, defineSchemaModel } from "@contractspec/lib.schema";
3
+ import { defineCommand, defineQuery } from "@contractspec/lib.contracts";
4
+ var OWNERS = ["platform.files"];
5
+ var FileModel = defineSchemaModel({
6
+ name: "File",
7
+ description: "Represents an uploaded file",
8
+ fields: {
9
+ id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
10
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
11
+ mimeType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
12
+ size: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
13
+ storageProvider: {
14
+ type: ScalarTypeEnum.String_unsecure(),
15
+ isOptional: false
16
+ },
17
+ storagePath: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
18
+ checksum: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
19
+ status: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
20
+ isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: false },
21
+ ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
22
+ orgId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
23
+ metadata: { type: ScalarTypeEnum.JSON(), isOptional: true },
24
+ width: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
25
+ height: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
26
+ createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
27
+ updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
28
+ }
29
+ });
30
+ var FileVersionModel = defineSchemaModel({
31
+ name: "FileVersion",
32
+ description: "Represents a file version",
33
+ fields: {
34
+ id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
35
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
36
+ version: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
37
+ size: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
38
+ storagePath: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
39
+ checksum: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
40
+ comment: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
41
+ createdBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
42
+ createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
43
+ }
44
+ });
45
+ var AttachmentModel = defineSchemaModel({
46
+ name: "Attachment",
47
+ description: "Represents an attachment",
48
+ fields: {
49
+ id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
50
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
51
+ entityType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
52
+ entityId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
53
+ attachmentType: {
54
+ type: ScalarTypeEnum.String_unsecure(),
55
+ isOptional: true
56
+ },
57
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
58
+ description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
59
+ order: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
60
+ metadata: { type: ScalarTypeEnum.JSON(), isOptional: true },
61
+ createdBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
62
+ createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
63
+ file: { type: FileModel, isOptional: true }
64
+ }
65
+ });
66
+ var PresignedUrlModel = defineSchemaModel({
67
+ name: "PresignedUrl",
68
+ description: "A presigned URL for file operations",
69
+ fields: {
70
+ url: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
71
+ fields: { type: ScalarTypeEnum.JSON(), isOptional: true },
72
+ expiresAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
73
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
74
+ sessionId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true }
75
+ }
76
+ });
77
+ var UploadFileInput = defineSchemaModel({
78
+ name: "UploadFileInput",
79
+ description: "Input for uploading a file",
80
+ fields: {
81
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
82
+ mimeType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
83
+ size: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
84
+ content: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
85
+ orgId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
86
+ isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: true },
87
+ metadata: { type: ScalarTypeEnum.JSON(), isOptional: true },
88
+ tags: { type: ScalarTypeEnum.JSON(), isOptional: true }
89
+ }
90
+ });
91
+ var UpdateFileInput = defineSchemaModel({
92
+ name: "UpdateFileInput",
93
+ description: "Input for updating a file",
94
+ fields: {
95
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
96
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
97
+ isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: true },
98
+ metadata: { type: ScalarTypeEnum.JSON(), isOptional: true },
99
+ tags: { type: ScalarTypeEnum.JSON(), isOptional: true }
100
+ }
101
+ });
102
+ var DeleteFileInput = defineSchemaModel({
103
+ name: "DeleteFileInput",
104
+ description: "Input for deleting a file",
105
+ fields: {
106
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false }
107
+ }
108
+ });
109
+ var GetFileInput = defineSchemaModel({
110
+ name: "GetFileInput",
111
+ description: "Input for getting a file",
112
+ fields: {
113
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false }
114
+ }
115
+ });
116
+ var ListFilesInput = defineSchemaModel({
117
+ name: "ListFilesInput",
118
+ description: "Input for listing files",
119
+ fields: {
120
+ orgId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
121
+ ownerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
122
+ mimeType: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
123
+ status: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
124
+ tags: { type: ScalarTypeEnum.JSON(), isOptional: true },
125
+ limit: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
126
+ offset: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true }
127
+ }
128
+ });
129
+ var ListFilesOutput = defineSchemaModel({
130
+ name: "ListFilesOutput",
131
+ description: "Output for listing files",
132
+ fields: {
133
+ files: { type: FileModel, isArray: true, isOptional: false },
134
+ total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false }
135
+ }
136
+ });
137
+ var GetDownloadUrlInput = defineSchemaModel({
138
+ name: "GetDownloadUrlInput",
139
+ description: "Input for getting a download URL",
140
+ fields: {
141
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
142
+ expiresInSeconds: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true }
143
+ }
144
+ });
145
+ var CreateVersionInput = defineSchemaModel({
146
+ name: "CreateVersionInput",
147
+ description: "Input for creating a file version",
148
+ fields: {
149
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
150
+ content: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
151
+ comment: { type: ScalarTypeEnum.String_unsecure(), isOptional: true }
152
+ }
153
+ });
154
+ var GetVersionsInput = defineSchemaModel({
155
+ name: "GetVersionsInput",
156
+ description: "Input for getting file versions",
157
+ fields: {
158
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
159
+ limit: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
160
+ offset: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true }
161
+ }
162
+ });
163
+ var GetVersionsOutput = defineSchemaModel({
164
+ name: "GetVersionsOutput",
165
+ description: "Output for getting file versions",
166
+ fields: {
167
+ versions: { type: FileVersionModel, isArray: true, isOptional: false },
168
+ total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false }
169
+ }
170
+ });
171
+ var AttachFileInput = defineSchemaModel({
172
+ name: "AttachFileInput",
173
+ description: "Input for attaching a file to an entity",
174
+ fields: {
175
+ fileId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
176
+ entityType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
177
+ entityId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
178
+ attachmentType: {
179
+ type: ScalarTypeEnum.String_unsecure(),
180
+ isOptional: true
181
+ },
182
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
183
+ description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
184
+ order: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
185
+ metadata: { type: ScalarTypeEnum.JSON(), isOptional: true }
186
+ }
187
+ });
188
+ var DetachFileInput = defineSchemaModel({
189
+ name: "DetachFileInput",
190
+ description: "Input for detaching a file",
191
+ fields: {
192
+ attachmentId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false }
193
+ }
194
+ });
195
+ var ListAttachmentsInput = defineSchemaModel({
196
+ name: "ListAttachmentsInput",
197
+ description: "Input for listing attachments",
198
+ fields: {
199
+ entityType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
200
+ entityId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
201
+ attachmentType: {
202
+ type: ScalarTypeEnum.String_unsecure(),
203
+ isOptional: true
204
+ }
205
+ }
206
+ });
207
+ var ListAttachmentsOutput = defineSchemaModel({
208
+ name: "ListAttachmentsOutput",
209
+ description: "Output for listing attachments",
210
+ fields: {
211
+ attachments: { type: AttachmentModel, isArray: true, isOptional: false },
212
+ total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false }
213
+ }
214
+ });
215
+ var CreatePresignedUrlInput = defineSchemaModel({
216
+ name: "CreatePresignedUrlInput",
217
+ description: "Input for creating a presigned upload URL",
218
+ fields: {
219
+ fileName: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
220
+ mimeType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
221
+ size: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
222
+ orgId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
223
+ expiresInSeconds: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true }
224
+ }
225
+ });
226
+ var SuccessOutput = defineSchemaModel({
227
+ name: "SuccessOutput",
228
+ description: "Generic success output",
229
+ fields: {
230
+ success: { type: ScalarTypeEnum.Boolean(), isOptional: false }
231
+ }
232
+ });
233
+ var UploadFileContract = defineCommand({
234
+ meta: {
235
+ key: "file.upload",
236
+ version: "1.0.0",
237
+ stability: "stable",
238
+ owners: [...OWNERS],
239
+ tags: ["files", "upload"],
240
+ description: "Upload a new file.",
241
+ goal: "Store a file and create a file record.",
242
+ context: "Called when uploading files directly."
243
+ },
244
+ io: {
245
+ input: UploadFileInput,
246
+ output: FileModel,
247
+ errors: {
248
+ FILE_TOO_LARGE: {
249
+ description: "File exceeds size limit",
250
+ http: 413,
251
+ gqlCode: "FILE_TOO_LARGE",
252
+ when: "File size exceeds configured limit"
253
+ },
254
+ INVALID_MIME_TYPE: {
255
+ description: "MIME type not allowed",
256
+ http: 415,
257
+ gqlCode: "INVALID_MIME_TYPE",
258
+ when: "File type is not in allowed list"
259
+ }
260
+ }
261
+ },
262
+ policy: {
263
+ auth: "user"
264
+ }
265
+ });
266
+ var UpdateFileContract = defineCommand({
267
+ meta: {
268
+ key: "file.update",
269
+ version: "1.0.0",
270
+ stability: "stable",
271
+ owners: [...OWNERS],
272
+ tags: ["files", "update"],
273
+ description: "Update file metadata.",
274
+ goal: "Modify file properties without replacing content.",
275
+ context: "Called when renaming or updating file metadata."
276
+ },
277
+ io: {
278
+ input: UpdateFileInput,
279
+ output: FileModel,
280
+ errors: {
281
+ FILE_NOT_FOUND: {
282
+ description: "File does not exist",
283
+ http: 404,
284
+ gqlCode: "FILE_NOT_FOUND",
285
+ when: "File ID is invalid"
286
+ }
287
+ }
288
+ },
289
+ policy: {
290
+ auth: "user"
291
+ }
292
+ });
293
+ var DeleteFileContract = defineCommand({
294
+ meta: {
295
+ key: "file.delete",
296
+ version: "1.0.0",
297
+ stability: "stable",
298
+ owners: [...OWNERS],
299
+ tags: ["files", "delete"],
300
+ description: "Delete a file.",
301
+ goal: "Remove a file and all its versions and attachments.",
302
+ context: "Called when removing a file permanently."
303
+ },
304
+ io: {
305
+ input: DeleteFileInput,
306
+ output: SuccessOutput,
307
+ errors: {
308
+ FILE_NOT_FOUND: {
309
+ description: "File does not exist",
310
+ http: 404,
311
+ gqlCode: "FILE_NOT_FOUND",
312
+ when: "File ID is invalid"
313
+ }
314
+ }
315
+ },
316
+ policy: {
317
+ auth: "user"
318
+ }
319
+ });
320
+ var GetFileContract = defineQuery({
321
+ meta: {
322
+ key: "file.get",
323
+ version: "1.0.0",
324
+ stability: "stable",
325
+ owners: [...OWNERS],
326
+ tags: ["files", "get"],
327
+ description: "Get a file by ID.",
328
+ goal: "Retrieve file metadata.",
329
+ context: "Called to inspect file details."
330
+ },
331
+ io: {
332
+ input: GetFileInput,
333
+ output: FileModel,
334
+ errors: {
335
+ FILE_NOT_FOUND: {
336
+ description: "File does not exist",
337
+ http: 404,
338
+ gqlCode: "FILE_NOT_FOUND",
339
+ when: "File ID is invalid"
340
+ }
341
+ }
342
+ },
343
+ policy: {
344
+ auth: "user"
345
+ }
346
+ });
347
+ var ListFilesContract = defineQuery({
348
+ meta: {
349
+ key: "file.list",
350
+ version: "1.0.0",
351
+ stability: "stable",
352
+ owners: [...OWNERS],
353
+ tags: ["files", "list"],
354
+ description: "List files with filtering.",
355
+ goal: "Browse uploaded files.",
356
+ context: "Called to browse file library."
357
+ },
358
+ io: {
359
+ input: ListFilesInput,
360
+ output: ListFilesOutput
361
+ },
362
+ policy: {
363
+ auth: "user"
364
+ }
365
+ });
366
+ var GetDownloadUrlContract = defineQuery({
367
+ meta: {
368
+ key: "file.downloadUrl",
369
+ version: "1.0.0",
370
+ stability: "stable",
371
+ owners: [...OWNERS],
372
+ tags: ["files", "download"],
373
+ description: "Get a presigned download URL.",
374
+ goal: "Generate a temporary URL for downloading.",
375
+ context: "Called when user wants to download a file."
376
+ },
377
+ io: {
378
+ input: GetDownloadUrlInput,
379
+ output: PresignedUrlModel,
380
+ errors: {
381
+ FILE_NOT_FOUND: {
382
+ description: "File does not exist",
383
+ http: 404,
384
+ gqlCode: "FILE_NOT_FOUND",
385
+ when: "File ID is invalid"
386
+ }
387
+ }
388
+ },
389
+ policy: {
390
+ auth: "user"
391
+ }
392
+ });
393
+ var CreateVersionContract = defineCommand({
394
+ meta: {
395
+ key: "file.version.create",
396
+ version: "1.0.0",
397
+ stability: "stable",
398
+ owners: [...OWNERS],
399
+ tags: ["files", "version", "create"],
400
+ description: "Create a new version of a file.",
401
+ goal: "Upload a new version while preserving history.",
402
+ context: "Called when updating a document."
403
+ },
404
+ io: {
405
+ input: CreateVersionInput,
406
+ output: FileVersionModel,
407
+ errors: {
408
+ FILE_NOT_FOUND: {
409
+ description: "File does not exist",
410
+ http: 404,
411
+ gqlCode: "FILE_NOT_FOUND",
412
+ when: "File ID is invalid"
413
+ }
414
+ }
415
+ },
416
+ policy: {
417
+ auth: "user"
418
+ }
419
+ });
420
+ var GetVersionsContract = defineQuery({
421
+ meta: {
422
+ key: "file.version.list",
423
+ version: "1.0.0",
424
+ stability: "stable",
425
+ owners: [...OWNERS],
426
+ tags: ["files", "version", "list"],
427
+ description: "Get file version history.",
428
+ goal: "View all versions of a file.",
429
+ context: "Called to browse file history."
430
+ },
431
+ io: {
432
+ input: GetVersionsInput,
433
+ output: GetVersionsOutput,
434
+ errors: {
435
+ FILE_NOT_FOUND: {
436
+ description: "File does not exist",
437
+ http: 404,
438
+ gqlCode: "FILE_NOT_FOUND",
439
+ when: "File ID is invalid"
440
+ }
441
+ }
442
+ },
443
+ policy: {
444
+ auth: "user"
445
+ }
446
+ });
447
+ var AttachFileContract = defineCommand({
448
+ meta: {
449
+ key: "attachment.attach",
450
+ version: "1.0.0",
451
+ stability: "stable",
452
+ owners: [...OWNERS],
453
+ tags: ["files", "attachment", "attach"],
454
+ description: "Attach a file to an entity.",
455
+ goal: "Link a file to a business entity.",
456
+ context: "Called when associating files with entities."
457
+ },
458
+ io: {
459
+ input: AttachFileInput,
460
+ output: AttachmentModel,
461
+ errors: {
462
+ FILE_NOT_FOUND: {
463
+ description: "File does not exist",
464
+ http: 404,
465
+ gqlCode: "FILE_NOT_FOUND",
466
+ when: "File ID is invalid"
467
+ },
468
+ ATTACHMENT_EXISTS: {
469
+ description: "Attachment already exists",
470
+ http: 409,
471
+ gqlCode: "ATTACHMENT_EXISTS",
472
+ when: "File is already attached to this entity"
473
+ }
474
+ }
475
+ },
476
+ policy: {
477
+ auth: "user"
478
+ }
479
+ });
480
+ var DetachFileContract = defineCommand({
481
+ meta: {
482
+ key: "attachment.detach",
483
+ version: "1.0.0",
484
+ stability: "stable",
485
+ owners: [...OWNERS],
486
+ tags: ["files", "attachment", "detach"],
487
+ description: "Detach a file from an entity.",
488
+ goal: "Remove a file association.",
489
+ context: "Called when removing file from entity."
490
+ },
491
+ io: {
492
+ input: DetachFileInput,
493
+ output: SuccessOutput,
494
+ errors: {
495
+ ATTACHMENT_NOT_FOUND: {
496
+ description: "Attachment does not exist",
497
+ http: 404,
498
+ gqlCode: "ATTACHMENT_NOT_FOUND",
499
+ when: "Attachment ID is invalid"
500
+ }
501
+ }
502
+ },
503
+ policy: {
504
+ auth: "user"
505
+ }
506
+ });
507
+ var ListAttachmentsContract = defineQuery({
508
+ meta: {
509
+ key: "attachment.list",
510
+ version: "1.0.0",
511
+ stability: "stable",
512
+ owners: [...OWNERS],
513
+ tags: ["files", "attachment", "list"],
514
+ description: "List attachments for an entity.",
515
+ goal: "Get all files attached to an entity.",
516
+ context: "Called to display attached files."
517
+ },
518
+ io: {
519
+ input: ListAttachmentsInput,
520
+ output: ListAttachmentsOutput
521
+ },
522
+ policy: {
523
+ auth: "user"
524
+ }
525
+ });
526
+ var CreatePresignedUrlContract = defineCommand({
527
+ meta: {
528
+ key: "file.presignedUrl.create",
529
+ version: "1.0.0",
530
+ stability: "stable",
531
+ owners: [...OWNERS],
532
+ tags: ["files", "presigned", "upload"],
533
+ description: "Create a presigned URL for direct upload.",
534
+ goal: "Enable direct-to-storage uploads.",
535
+ context: "Called for large file uploads."
536
+ },
537
+ io: {
538
+ input: CreatePresignedUrlInput,
539
+ output: PresignedUrlModel,
540
+ errors: {
541
+ FILE_TOO_LARGE: {
542
+ description: "File exceeds size limit",
543
+ http: 413,
544
+ gqlCode: "FILE_TOO_LARGE",
545
+ when: "Requested file size exceeds limit"
546
+ },
547
+ INVALID_MIME_TYPE: {
548
+ description: "MIME type not allowed",
549
+ http: 415,
550
+ gqlCode: "INVALID_MIME_TYPE",
551
+ when: "File type is not in allowed list"
552
+ }
553
+ }
554
+ },
555
+ policy: {
556
+ auth: "user"
557
+ }
558
+ });
559
+ export {
560
+ UploadFileContract,
561
+ UpdateFileContract,
562
+ PresignedUrlModel,
563
+ ListFilesContract,
564
+ ListAttachmentsContract,
565
+ GetVersionsContract,
566
+ GetFileContract,
567
+ GetDownloadUrlContract,
568
+ FileVersionModel,
569
+ FileModel,
570
+ DetachFileContract,
571
+ DeleteFileContract,
572
+ CreateVersionContract,
573
+ CreatePresignedUrlContract,
574
+ AttachmentModel,
575
+ AttachFileContract
576
+ };
@@ -0,0 +1,65 @@
1
+ // src/docs/files.docblock.ts
2
+ import { registerDocBlocks } from "@contractspec/lib.contracts/docs";
3
+ var filesDocBlocks = [
4
+ {
5
+ id: "docs.files.attachments",
6
+ title: "Files, Versions & Attachments",
7
+ summary: "Spec-first file management with storage adapters, versioning, presigned URLs, and polymorphic attachments for any entity.",
8
+ kind: "reference",
9
+ visibility: "public",
10
+ route: "/docs/files/attachments",
11
+ tags: ["files", "attachments", "storage", "versions"],
12
+ body: `## Capabilities
13
+
14
+ - **Entities**: File, FileVersion, Attachment, UploadSession with retention, checksum, ACLs.
15
+ - **Contracts**: upload/update/delete/list/get files; presigned upload/download; version create/list; attach/detach/list attachments.
16
+ - **Storage**: pluggable adapters (Local, S3 placeholder + interface), in-memory for tests.
17
+ - **Events**: file.uploaded/deleted, attachment.added/removed (see events export).
18
+
19
+ ## Usage
20
+
21
+ 1) Compose schema
22
+ - Include \`filesSchemaContribution\` in your schema composition.
23
+
24
+ 2) Register contracts/events
25
+ - Import contracts and events from \`@contractspec/lib.files\` in your spec registry.
26
+
27
+ 3) Wire storage
28
+ - Provide a \`StorageAdapter\` implementation (local/in-memory or S3 via custom impl).
29
+ - Use \`createStorageAdapter\` with config to instantiate.
30
+
31
+ 4) Attach to domain entities
32
+ - Use \`attachment.attach\` with \`entityType/entityId\` to link files to deals, orders, runs, etc.
33
+
34
+ ## Example
35
+
36
+ ${"```"}ts
37
+ import {
38
+ UploadFileContract,
39
+ AttachFileContract,
40
+ InMemoryStorageAdapter,
41
+ } from '@contractspec/lib.files';
42
+
43
+ // storage
44
+ const storage = new InMemoryStorageAdapter();
45
+
46
+ // upload
47
+ const file = await storage.upload({
48
+ path: 'org-1/reports/r1.pdf',
49
+ content: Buffer.from(pdfBytes),
50
+ mimeType: 'application/pdf',
51
+ });
52
+
53
+ // attach
54
+ await AttachFileContract; // register in spec to enable attach flows
55
+ ${"```"},
56
+
57
+ ## Guardrails
58
+
59
+ - Enforce size/MIME limits in your handlers; avoid storing PII in paths.
60
+ - Keep \`orgId\` scoped for multi-tenant isolation; prefer presigned URLs for public delivery.
61
+ - Persist checksums for integrity; emit audit + events for access/retention changes.
62
+ `
63
+ }
64
+ ];
65
+ registerDocBlocks(filesDocBlocks);
@@ -0,0 +1,65 @@
1
+ // src/docs/files.docblock.ts
2
+ import { registerDocBlocks } from "@contractspec/lib.contracts/docs";
3
+ var filesDocBlocks = [
4
+ {
5
+ id: "docs.files.attachments",
6
+ title: "Files, Versions & Attachments",
7
+ summary: "Spec-first file management with storage adapters, versioning, presigned URLs, and polymorphic attachments for any entity.",
8
+ kind: "reference",
9
+ visibility: "public",
10
+ route: "/docs/files/attachments",
11
+ tags: ["files", "attachments", "storage", "versions"],
12
+ body: `## Capabilities
13
+
14
+ - **Entities**: File, FileVersion, Attachment, UploadSession with retention, checksum, ACLs.
15
+ - **Contracts**: upload/update/delete/list/get files; presigned upload/download; version create/list; attach/detach/list attachments.
16
+ - **Storage**: pluggable adapters (Local, S3 placeholder + interface), in-memory for tests.
17
+ - **Events**: file.uploaded/deleted, attachment.added/removed (see events export).
18
+
19
+ ## Usage
20
+
21
+ 1) Compose schema
22
+ - Include \`filesSchemaContribution\` in your schema composition.
23
+
24
+ 2) Register contracts/events
25
+ - Import contracts and events from \`@contractspec/lib.files\` in your spec registry.
26
+
27
+ 3) Wire storage
28
+ - Provide a \`StorageAdapter\` implementation (local/in-memory or S3 via custom impl).
29
+ - Use \`createStorageAdapter\` with config to instantiate.
30
+
31
+ 4) Attach to domain entities
32
+ - Use \`attachment.attach\` with \`entityType/entityId\` to link files to deals, orders, runs, etc.
33
+
34
+ ## Example
35
+
36
+ ${"```"}ts
37
+ import {
38
+ UploadFileContract,
39
+ AttachFileContract,
40
+ InMemoryStorageAdapter,
41
+ } from '@contractspec/lib.files';
42
+
43
+ // storage
44
+ const storage = new InMemoryStorageAdapter();
45
+
46
+ // upload
47
+ const file = await storage.upload({
48
+ path: 'org-1/reports/r1.pdf',
49
+ content: Buffer.from(pdfBytes),
50
+ mimeType: 'application/pdf',
51
+ });
52
+
53
+ // attach
54
+ await AttachFileContract; // register in spec to enable attach flows
55
+ ${"```"},
56
+
57
+ ## Guardrails
58
+
59
+ - Enforce size/MIME limits in your handlers; avoid storing PII in paths.
60
+ - Keep \`orgId\` scoped for multi-tenant isolation; prefer presigned URLs for public delivery.
61
+ - Persist checksums for integrity; emit audit + events for access/retention changes.
62
+ `
63
+ }
64
+ ];
65
+ registerDocBlocks(filesDocBlocks);