@gullabs/google 0.8.4 → 0.9.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/README.md CHANGED
@@ -23,6 +23,19 @@ pnpm add @gullabs/google @gullabs/core @google/genai
23
23
  | `geminiModelDescriptors`, `gemmaModelDescriptors`, `defaultGeminiRegistry` | Built-in model descriptors + pre-built registry |
24
24
  | `geminiPricingSource()`, `GEMINI_PRICING`, `TIER_FACTOR` | Built-in Gemini pricing snapshot and tier-factor map |
25
25
 
26
+
27
+ | `GoogleFileStore` | Files API: upload + poll ACTIVE + delete |
28
+ | `FileDeleteOptions` | `{ failClosed?, signal? }` — opt-in fail-closed delete (parity with `@gullabs/xai`) |
29
+
30
+ ## File store delete modes
31
+
32
+ `GoogleFileStore.delete` defaults to **fail-open** (errors → `onDeleteError`, resolve). Pass `{ failClosed: true }` when the host gates durable state on known success; HTTP/SDK not-found remains success (idempotent). Empty `handle.name` always throws `bad_request`.
33
+
34
+ ```ts
35
+ await store.delete(handle) // fail-open
36
+ await store.delete(handle, { failClosed: true }) // throw on non-not-found failure
37
+ ```
38
+
26
39
  ## Quick example
27
40
 
28
41
  ```ts
package/dist/index.cjs CHANGED
@@ -2873,25 +2873,88 @@ var GoogleFileStore = class {
2873
2873
  }
2874
2874
  }
2875
2875
  /**
2876
- * Delete a single uploaded file.
2877
- * Errors are forwarded to `onDeleteError` and NOT rethrown.
2876
+ * Delete a single uploaded file. Idempotent: not-found → success.
2877
+ *
2878
+ * Default (`failClosed` omitted/false): errors go to `onDeleteError` and
2879
+ * resolve (P5 fail-open). With `failClosed: true`, non-not-found errors
2880
+ * throw typed `LlmError` and `onDeleteError` is not called.
2881
+ *
2882
+ * Empty/blank `handle.name` always throws `bad_request`.
2878
2883
  */
2879
- async delete(handle) {
2884
+ async delete(handle, opts) {
2885
+ const name = handle.name;
2886
+ if (typeof name !== "string" || name.trim() === "") {
2887
+ throw new core.LlmError("GoogleFileHandle.name must be a non-empty string.", {
2888
+ kind: "bad_request",
2889
+ retryable: false,
2890
+ provider: "google"
2891
+ });
2892
+ }
2893
+ const failClosed = opts?.failClosed === true;
2880
2894
  try {
2895
+ if (opts?.signal?.aborted === true) {
2896
+ throw new core.LlmError("Google file delete aborted", {
2897
+ kind: "aborted",
2898
+ retryable: false,
2899
+ provider: "google"
2900
+ });
2901
+ }
2881
2902
  const client = await this.getClient();
2882
- await client.delete({ name: handle.name });
2903
+ await client.delete({ name });
2883
2904
  } catch (err) {
2884
- this.onDeleteError(handle.name, err);
2905
+ if (isGoogleNotFoundError(err)) {
2906
+ return;
2907
+ }
2908
+ const classified = err instanceof core.LlmError ? err : core.classifyError(err);
2909
+ const withProvider = classified.provider === void 0 ? new core.LlmError(classified.message, {
2910
+ kind: classified.kind,
2911
+ retryable: classified.retryable,
2912
+ ...classified.httpStatus !== void 0 ? { httpStatus: classified.httpStatus } : {},
2913
+ ...classified.retryAfterMs !== void 0 ? { retryAfterMs: classified.retryAfterMs } : {},
2914
+ provider: "google",
2915
+ cause: classified.cause ?? err
2916
+ }) : classified;
2917
+ if (failClosed) {
2918
+ throw withProvider;
2919
+ }
2920
+ this.onDeleteError(name, withProvider);
2885
2921
  }
2886
2922
  }
2887
2923
  /**
2888
2924
  * Delete multiple files.
2889
- * Each failure is individually forwarded to `onDeleteError`; none are thrown.
2925
+ *
2926
+ * Fail-open (default): `Promise.allSettled` per handle.
2927
+ * Fail-closed: `Promise.all` — first throw rejects; in-flight siblings are
2928
+ * not cancelled. Prefer per-handle delete when gating durable release state.
2890
2929
  */
2891
- async deleteAll(handles) {
2892
- await Promise.allSettled(handles.map((h) => this.delete(h)));
2930
+ async deleteAll(handles, opts) {
2931
+ if (opts?.failClosed === true) {
2932
+ await Promise.all(handles.map((h) => this.delete(h, opts)));
2933
+ return;
2934
+ }
2935
+ await Promise.allSettled(handles.map((h) => this.delete(h, opts)));
2893
2936
  }
2894
2937
  };
2938
+ function isGoogleNotFoundError(err) {
2939
+ if (err instanceof core.LlmError && err.httpStatus === 404) {
2940
+ return true;
2941
+ }
2942
+ if (typeof err !== "object" || err === null) {
2943
+ return false;
2944
+ }
2945
+ const obj = err;
2946
+ if (obj["status"] === 404 || obj["httpStatus"] === 404 || obj["code"] === 404) {
2947
+ return true;
2948
+ }
2949
+ if (obj["status"] === "NOT_FOUND" || obj["code"] === "NOT_FOUND") {
2950
+ return true;
2951
+ }
2952
+ const msg = typeof obj["message"] === "string" ? obj["message"] : "";
2953
+ if (/not\s*found|404/i.test(msg) && /file/i.test(msg)) {
2954
+ return true;
2955
+ }
2956
+ return false;
2957
+ }
2895
2958
  var DEFAULT_SKEW_SECONDS = 30;
2896
2959
  var DEFAULT_EXTENSION_SECONDS = 3600;
2897
2960
  async function buildCachesClient(auth) {