@voltro/plugin-storage 0.71.0 → 0.72.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.
package/dist/index.d.ts CHANGED
@@ -318,6 +318,49 @@ export declare interface ListRefsResult {
318
318
  readonly nextOffset: number | null;
319
319
  }
320
320
 
321
+ /** DataStore-backed metadata boundary, intentionally no provider access. The
322
+ * service must use reserve -> provider.put -> confirm and retain failed intents. */
323
+ export declare const makeStorageObjectMetadata: (store: DataStore) => {
324
+ /** The put intent a reference was written under, if it was written through an operation at all. */
325
+ intent: (refId: string) => Promise<{
326
+ identity: {
327
+ readonly tenantId: string | null;
328
+ readonly bucket: string;
329
+ readonly fingerprint: string;
330
+ readonly checksum: string;
331
+ readonly operationId: string;
332
+ readonly logicalKey: string;
333
+ };
334
+ ref: StorageRef;
335
+ } | undefined>;
336
+ reserve: (identity: StorageObjectIdentity, proposed: StorageRef, quota?: StorageQuota) => Promise<{
337
+ ref: StorageRef;
338
+ ready: boolean;
339
+ }>;
340
+ confirm: (identity: StorageObjectIdentity) => Promise<StorageRef>;
341
+ beginDelete: (refId: string, tenantId?: string | null) => Promise<{
342
+ operation: {
343
+ readonly id: string;
344
+ readonly key: string;
345
+ readonly fingerprint: string;
346
+ readonly generation: string;
347
+ readonly state: "ready" | "reserved" | "deleting" | "deleted";
348
+ readonly refId: string;
349
+ readonly objectId: string;
350
+ readonly deleteKey: string | null;
351
+ };
352
+ identity: {
353
+ readonly tenantId: string | null;
354
+ readonly bucket: string;
355
+ readonly fingerprint: string;
356
+ readonly checksum: string;
357
+ readonly operationId: string;
358
+ readonly logicalKey: string;
359
+ };
360
+ }>;
361
+ confirmDelete: (identity: StorageObjectIdentity) => Promise<boolean>;
362
+ };
363
+
321
364
  export declare const memoryGrantStore: () => GrantStore;
322
365
 
323
366
  export declare const memoryProvider: (instance?: string) => StorageProvider;
@@ -400,6 +443,17 @@ export declare interface PutInput {
400
443
  readonly derivedFrom?: string;
401
444
  /** Derivative label — `'rendition'` / `'poster'` (internal). */
402
445
  readonly kind?: string;
446
+ /**
447
+ * The RESUMABLE identity of this put. Two calls with the same `operationId`
448
+ * (a retry after a network failure, two replicas completing the same job)
449
+ * resolve to ONE reference: the second finds the first's reservation and
450
+ * returns its ref, or finishes what the first left half-done. Without it a
451
+ * put is a fresh object every time — and a retried put beside its winner
452
+ * left a second reference on the same key whose bytes outlived the winner's
453
+ * delete. Any stable string the caller owns (a job id, a message id).
454
+ * Needs a bound data store; the in-memory ref store cannot hold intents.
455
+ */
456
+ readonly operationId?: string;
403
457
  }
404
458
 
405
459
  /**
@@ -622,7 +676,28 @@ export declare type StorageGuard = (ctx: {
622
676
  readonly ref: StorageRef;
623
677
  }) => boolean | Promise<boolean> | Effect.Effect<boolean>;
624
678
 
625
- declare interface StorageObjectIdentity {
679
+ /**
680
+ * The instance name an upload ticket must carry, for THIS installation.
681
+ *
682
+ * `signUploadTicket` takes `instance: string`, and the upload route compares it
683
+ * against the plugin's own instance — so a wrong value is not a type error, it
684
+ * is a bare `401` on every upload. The value has always been derivable
685
+ * (`storagePlugin(options).name` is this same expression), but nothing said so
686
+ * and `BASE_NAME` is not exported, so a caller minting a ticket outside the
687
+ * plugin had to guess it. Guessing is exactly what a `string` parameter invites.
688
+ *
689
+ * const ticket = signUploadTicket(
690
+ * { instance: storageInstanceName(), key, contentType },
691
+ * 300,
692
+ * )
693
+ *
694
+ * Pass the SAME options the installation was built with — `alias` and `name`
695
+ * both move the instance, and a ticket minted for the default name does not
696
+ * open a door installed under an alias.
697
+ */
698
+ export declare const storageInstanceName: (options?: StoragePluginOptions) => string;
699
+
700
+ export declare interface StorageObjectIdentity {
626
701
  readonly tenantId: string | null;
627
702
  readonly bucket: string;
628
703
  readonly operationId: string;
@@ -634,6 +709,29 @@ declare interface StorageObjectIdentity {
634
709
  readonly logicalKey: string;
635
710
  }
636
711
 
712
+ /** Internal persistence substrate. Service wiring must commit quota and refs in
713
+ * the same transaction as these transitions; never run provider I/O here. */
714
+ export declare const storageObjectLifecycleTables: ( Table<"_voltro_storage_operation_locks", FieldDefinitions<{
715
+ readonly id: ColumnBuilder<string, "text", boolean>;
716
+ readonly nonce: ColumnBuilder<string, "text", boolean>;
717
+ }>, false, never> | Table<"_voltro_storage_object_heads", FieldDefinitions<{
718
+ readonly id: ColumnBuilder<string, "text", boolean>;
719
+ readonly generation: ColumnBuilder<string, "text", boolean>;
720
+ readonly key: ColumnBuilder<string, "text", boolean>;
721
+ readonly reservations: ColumnBuilder<number, "integer", boolean>;
722
+ }>, false, never> | Table<"_voltro_storage_operations", FieldDefinitions<{
723
+ readonly id: ColumnBuilder<string, "text", boolean>;
724
+ readonly objectId: ColumnBuilder<string, "text", boolean>;
725
+ readonly fingerprint: ColumnBuilder<string, "text", boolean>;
726
+ readonly generation: ColumnBuilder<string, "text", boolean>;
727
+ readonly key: ColumnBuilder<string, "text", boolean>;
728
+ readonly refId: ColumnBuilder<string, "text", boolean>;
729
+ readonly state: ColumnBuilder<string, "text", boolean>;
730
+ readonly deleteKey: ColumnBuilder<string | null, "text", boolean>;
731
+ }>, false, never>)[];
732
+
733
+ export declare type StorageObjectMetadata = ReturnType<typeof makeStorageObjectMetadata>;
734
+
637
735
  export declare const storagePlugin: (options?: StoragePluginOptions) => VoltroPlugin;
638
736
 
639
737
  export declare interface StoragePluginOptions {
@@ -986,6 +1084,11 @@ export declare interface StorageServiceOptions {
986
1084
  * the cap — and refunded if the write fails. Exceeding it fails with
987
1085
  * `StorageRejected` (reason `'quota-exceeded'`) before any bytes land. */
988
1086
  readonly quota?: StorageQuota;
1087
+ /** The durable object-operation boundary a `put` with an `operationId` and
1088
+ * the matching `delete` go through. Read at call time: the plugin binds it
1089
+ * once the app's data store exists, and `undefined` before that means an
1090
+ * `operationId` put is refused rather than silently made non-resumable. */
1091
+ readonly objects?: () => StorageObjectMetadata | undefined;
989
1092
  }
990
1093
 
991
1094
  /** The service handlers consume: `const storage = yield* StorageService`. */
@@ -1108,6 +1211,8 @@ export declare interface StorageServiceShape {
1108
1211
  readonly abortSignal?: AbortSignal;
1109
1212
  readonly contentType?: string;
1110
1213
  readonly tenantId?: string | null;
1214
+ /** The resumable identity of this ingest — see `PutInput.operationId`. */
1215
+ readonly operationId?: string;
1111
1216
  readonly ownerId?: string | null;
1112
1217
  readonly visibility?: StorageVisibility;
1113
1218
  readonly key?: string;