@opengeni/db 3.1.3-canary.38890816741001 → 3.2.2

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/src/vaults.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  type VaultMode,
15
15
  type VaultOwnerKind,
16
16
  type VaultPolicy,
17
+ type VaultProviderOperations,
17
18
  type VaultSource,
18
19
  type VaultSyncStatus,
19
20
  type VaultVersionCreatedByKind,
@@ -136,6 +137,16 @@ function json(value: unknown): string | null {
136
137
  // Row shapes (as the routines return them; values encrypted)
137
138
  // ---------------------------------------------------------------------------
138
139
 
140
+ /**
141
+ * What a provider (or its bridge) said about its own upstream freshness at
142
+ * the last successful check (0789). Opengeni's own check time is
143
+ * `lastSyncedAt`; it never stands in for `sourceSyncedAt`.
144
+ */
145
+ export type StoredProviderFreshness = {
146
+ source: "live" | "cache" | "unknown";
147
+ sourceSyncedAt: string | null;
148
+ };
149
+
139
150
  export type VaultRow = {
140
151
  id: string;
141
152
  workspaceId: string;
@@ -153,6 +164,15 @@ export type VaultRow = {
153
164
  rateLimitedUntil: string | null;
154
165
  providerConnectionId: string | null;
155
166
  providerVaultRef: string | null;
167
+ /** Effective operations the provider last reported (0789); null before the first check. */
168
+ providerOperations?: VaultProviderOperations | null;
169
+ /** The provider's own freshness observation from the last successful check (0789). */
170
+ providerFreshness?: StoredProviderFreshness | null;
171
+ /**
172
+ * Durable value-cache epoch (0789, bigint as text). Every complete mirror
173
+ * replacement advances it; provider value caches compare it before reuse.
174
+ */
175
+ providerCacheEpoch?: string;
156
176
  availability: { allowedWorkspaceIds: string[] | null; allowPersonalWorkspaces: boolean } | null;
157
177
  isDefault: boolean;
158
178
  itemCount: number;
@@ -222,6 +242,8 @@ export type VaultFieldCiphertext = {
222
242
  source: VaultSource;
223
243
  providerConnectionId: string | null;
224
244
  providerVaultRef: string | null;
245
+ /** The vault's value-cache epoch (0789). */
246
+ providerCacheEpoch?: string;
225
247
  providerItemRef: string | null;
226
248
  providerFieldRef: string | null;
227
249
  };
@@ -263,6 +285,8 @@ export type SecretReferenceCandidate = {
263
285
  source: VaultSource;
264
286
  providerConnectionId: string | null;
265
287
  providerVaultRef: string | null;
288
+ /** The vault's value-cache epoch (0789). */
289
+ providerCacheEpoch?: string;
266
290
  itemId: string;
267
291
  itemName: string;
268
292
  itemType: VaultItemType;
@@ -752,6 +776,12 @@ export type ProviderVaultItemMirror = {
752
776
  fields: Array<{ providerFieldRef: string; name: string; kind: VaultFieldKind }>;
753
777
  };
754
778
 
779
+ /**
780
+ * Replace a connected vault's metadata mirror with one complete inventory
781
+ * (at most `VAULT_LIMITS.itemsPerVault` items; more is refused with
782
+ * `provider_inventory_incomplete` and the old mirror kept). Items missing
783
+ * from `items` are removed, and the vault's cache epoch advances (0789).
784
+ */
755
785
  export async function upsertProviderVaultItems(
756
786
  db: Database,
757
787
  context: VaultDbContext,
@@ -765,6 +795,13 @@ export async function upsertProviderVaultItems(
765
795
  });
766
796
  }
767
797
 
798
+ /**
799
+ * Record a connected vault's sync state. `operations` and `freshness` are
800
+ * optional (0789): absent keeps the stored value, null clears it. The optional
801
+ * mirror and status settle atomically, comparing `expectedCacheEpoch` under
802
+ * the vault lock; a mismatch raises provider_conflict with superseded=true.
803
+ * Every applied settlement advances the epoch, even unchanged or failed checks.
804
+ */
768
805
  export async function setVaultSyncStatus(
769
806
  db: Database,
770
807
  context: VaultDbContext,
@@ -774,6 +811,14 @@ export async function setVaultSyncStatus(
774
811
  errorCode?: string | null;
775
812
  errorMessage?: string | null;
776
813
  rateLimitedUntil?: string | null;
814
+ /** The provider's effective operations for this vault. */
815
+ operations?: VaultProviderOperations | null;
816
+ /** The provider's own upstream freshness observation (never Opengeni's check time). */
817
+ freshness?: StoredProviderFreshness | null;
818
+ /** Reject a late refresh when another settlement advanced this epoch. */
819
+ expectedCacheEpoch?: string;
820
+ /** Complete inventory committed atomically with this successful status. */
821
+ mirror?: { contentVersion: string | null; items: ProviderVaultItemMirror[] };
777
822
  },
778
823
  ): Promise<void> {
779
824
  const { vaultId, ...status } = input;
@@ -784,7 +829,16 @@ export async function setVaultSyncStatus(
784
829
  });
785
830
  }
786
831
 
787
- /** Link a 1Password or host vault (W5). The mirror is filled by the first sync. */
832
+ /**
833
+ * Link a connected vault (W5, 0789). Every source but `host` needs an active
834
+ * `api_key` connection in the caller's workspace whose provider identity
835
+ * matches (`1password.com` with the 1Password service-account role, or
836
+ * `vault:<source>` with the `external_vault_provider` role and
837
+ * `metadata.vaultProvider.provider = source`, version 1, not pending
838
+ * verification) and whose ownership matches `ownerKind`: a personal
839
+ * connection backs only its owner's Only-me vaults. The mirror is filled by
840
+ * the first sync.
841
+ */
788
842
  export async function linkProviderVault(
789
843
  db: Database,
790
844
  context: VaultDbContext,
@@ -792,7 +846,7 @@ export async function linkProviderVault(
792
846
  ownerKind: "user" | "workspace" | "organization";
793
847
  name: string;
794
848
  description?: string | null;
795
- source: "onepassword" | "host";
849
+ source: Exclude<VaultSource, "opengeni">;
796
850
  providerConnectionId?: string | null;
797
851
  providerVaultRef: string;
798
852
  mode: VaultMode;
@@ -809,6 +863,32 @@ export async function linkProviderVault(
809
863
  );
810
864
  }
811
865
 
866
+ /**
867
+ * Exercise the exact link policy before an irreversible provider create. The
868
+ * temporary link, personal authority and audit rows are all rolled back. This
869
+ * is a preflight, not a reservation: a race after it must return the confirmed
870
+ * upstream identity so the caller can link that vault instead of creating again.
871
+ * No database transaction is held open across provider I/O.
872
+ */
873
+ export async function preflightProviderVaultLink(
874
+ db: Database,
875
+ context: VaultDbContext,
876
+ input: Omit<Parameters<typeof linkProviderVault>[2], "providerVaultRef">,
877
+ ): Promise<void> {
878
+ const passed = new Error("vault link preflight passed");
879
+ try {
880
+ await db.transaction(async (tx) => {
881
+ await linkProviderVault(tx as unknown as Database, context, {
882
+ ...input,
883
+ providerVaultRef: `preflight:${crypto.randomUUID()}`,
884
+ });
885
+ throw passed;
886
+ });
887
+ } catch (error) {
888
+ if (error !== passed) throw error;
889
+ }
890
+ }
891
+
812
892
  export type ProviderVaultSyncTargetRow = {
813
893
  vaultId: string;
814
894
  workspaceId: string;
@@ -818,6 +898,8 @@ export type ProviderVaultSyncTargetRow = {
818
898
  providerVaultRef: string;
819
899
  providerContentVersion: string | null;
820
900
  rateLimitedUntil: string | null;
901
+ /** The vault's value-cache epoch (0789). */
902
+ providerCacheEpoch?: string;
821
903
  };
822
904
 
823
905
  /** The provider refs and mirror version of one reachable connected vault. */