@oberik/sdk 0.66.0 → 0.68.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/esm/index.js CHANGED
@@ -758,6 +758,25 @@ function asBlob(input) {
758
758
  // handing that straight to Blob can carry bytes belonging to something else.
759
759
  return new Blob([bytes.slice()]);
760
760
  }
761
+ /**
762
+ * The name a document is stored under: what the caller passed, the file's own, or a
763
+ * placeholder.
764
+ *
765
+ * One function for both clients, because they disagreed about it. The project client
766
+ * derived a name and fell back to `"upload.bin"`; the end-user client took `filename` in a
767
+ * **required** options object, so a JavaScript caller writing the documented
768
+ * `uploadAndWait(file)` got `TypeError: Cannot read properties of undefined (reading
769
+ * 'filename')` — thrown from inside the SDK, with nothing in the stack naming Oberik, at
770
+ * the reader most likely to have copied the snippet (OBE-195).
771
+ *
772
+ * Both are optional now and both land here. `"upload.bin"` rather than a refusal: a `File`
773
+ * carries its name and a Blob, Buffer or ArrayBuffer does not, and an upload that arrives
774
+ * with a placeholder name is visibly wrong in the listing, where a caller can fix it with
775
+ * `documents.update`. Pass `filename` — it is what the document is listed and cited under.
776
+ */
777
+ function uploadName(file, filename) {
778
+ return filename || file?.name || "upload.bin";
779
+ }
761
780
  /** Normalise whatever an `onQuestion` handler returned into an answer the API accepts.
762
781
  *
763
782
  * A handler returning a bare string was silently ignored. `{ ...answer }` on a string
@@ -2363,12 +2382,18 @@ export class AgentFramework {
2363
2382
  delete: (id) => this.request("DELETE", `/documents/${id}`),
2364
2383
  retrieve: (body) => this.request("POST", "/documents/retrieve", { body }),
2365
2384
  /** Small-file convenience upload via multipart form (server proxies to S3). */
2366
- uploadSimple: async (file, opts) => {
2385
+ uploadSimple: async (file,
2386
+ // Optional, and the same shape the project client takes: this signature required an
2387
+ // options object, so `uploadSimple(file)` and `uploadAndWait(file)` — the way the
2388
+ // docs write them — threw a TypeError out of the SDK's own body instead of saying
2389
+ // what was missing (OBE-195).
2390
+ opts = {}) => {
2391
+ const filename = uploadName(file, opts.filename);
2367
2392
  const form = new FormData();
2368
2393
  const blob = typeof Blob !== "undefined" && file instanceof Blob
2369
2394
  ? file
2370
2395
  : new Blob([file], { type: opts.contentType ?? "application/octet-stream" });
2371
- form.append("file", blob, opts.filename);
2396
+ form.append("file", blob, filename);
2372
2397
  form.append("tags", (opts.tags ?? []).join(","));
2373
2398
  // Set visibility AT UPLOAD, not after: a document is retrievable as soon as
2374
2399
  // ingestion finishes, so patching later leaves a window where it's readable by
@@ -2401,7 +2426,7 @@ export class AgentFramework {
2401
2426
  return (await res.json());
2402
2427
  },
2403
2428
  /** Resumable presigned multipart upload (direct to S3). */
2404
- upload: (file, opts) => this.multipartUpload(file, opts),
2429
+ upload: (file, opts = {}) => this.multipartUpload(file, opts),
2405
2430
  /** Poll a document until ingestion finishes (status "ready" or "failed").
2406
2431
  * Ingestion is async, so querying a just-uploaded doc may return nothing until
2407
2432
  * this resolves. Throws on "failed" or timeout. */
@@ -2425,7 +2450,7 @@ export class AgentFramework {
2425
2450
  // Takes the ACL fields too: this is the upload most callers use, so omitting them
2426
2451
  // here forced a second `patch` and left a window where the document was already
2427
2452
  // retrievable at its default visibility.
2428
- opts) => {
2453
+ opts = {}) => {
2429
2454
  const doc = await this.documents.uploadSimple(file, opts);
2430
2455
  return this.documents.waitReady(doc.id, { timeoutMs: opts.timeoutMs, signal: opts.signal });
2431
2456
  },
@@ -3086,7 +3111,7 @@ export class AgentFramework {
3086
3111
  const maxRetries = opts.maxRetries ?? 5;
3087
3112
  const presign = await this.request("POST", "/documents/presign-upload", {
3088
3113
  body: {
3089
- filename: opts.filename,
3114
+ filename: uploadName(file, opts.filename),
3090
3115
  content_type: opts.contentType,
3091
3116
  tags: opts.tags ?? [],
3092
3117
  ...(opts.visibility ? { visibility: opts.visibility } : {}),
@@ -3365,6 +3390,9 @@ export class OberikProject {
3365
3390
  * turning citation markers off, meant dropping to raw HTTP through `raw()`.
3366
3391
  */
3367
3392
  project = {
3393
+ /** The project, its capability ceiling and its settings — plus `readiness` and the
3394
+ * `health` / `healthDetail` badge, so "is this project working" is answered by the
3395
+ * first thing you fetch rather than one route further on. */
3368
3396
  get: () => this.request("GET", ""),
3369
3397
  /** Merges — send only what you want to change. */
3370
3398
  set: (fields) => this.request("PATCH", "", fields),
@@ -3438,9 +3466,26 @@ export class OberikProject {
3438
3466
  * accepted nor needed — a recipe that worked by luck rather than by expression.
3439
3467
  */
3440
3468
  documents = {
3441
- /** Same filters as the end-user client's `documents.list`, so the two agree. */
3469
+ /**
3470
+ * One PAGE of the corpus — the same envelope, the same filters and the same paging as
3471
+ * the end-user client's `documents.list`, because they are the same route.
3472
+ *
3473
+ * The route became a page in OBE-190 and this declaration did not move with it: it
3474
+ * still said `Promise<DocumentOut[]>`, so a TypeScript caller wrote `.length` and got
3475
+ * `undefined` with no type error and no exception — a census that counted documents
3476
+ * reported zero. That is OBE-147 verbatim, one collection over, and the docstring
3477
+ * describing it sits sixty lines above this one (OBE-195). The query type had the
3478
+ * mirror-image gap: `limit`/`offset` worked at runtime and were unwritable in
3479
+ * TypeScript, so the documented paging loop did not compile against the client that
3480
+ * recommends it.
3481
+ *
3482
+ * `with_total` adds a `total`; it is what "how big is this corpus" is asked with, and
3483
+ * it costs a second query. "Is there another page" is `has_more`, and it is free.
3484
+ */
3442
3485
  list: (query = {}) => {
3443
- const qs = new URLSearchParams(Object.entries(query).filter(([, v]) => v != null)).toString();
3486
+ const qs = new URLSearchParams(Object.entries(query)
3487
+ .filter(([, v]) => v != null)
3488
+ .map(([k, v]) => [k, String(v)])).toString();
3444
3489
  return this.request("GET", `/documents${qs ? `?${qs}` : ""}`);
3445
3490
  },
3446
3491
  get: (documentId) => this.request("GET", `/documents/${documentId}`),
@@ -3485,8 +3530,7 @@ export class OberikProject {
3485
3530
  */
3486
3531
  opts = {}) => {
3487
3532
  const form = new FormData();
3488
- const name = opts.filename ?? file.name ?? "upload.bin";
3489
- form.append("file", asBlob(file), name);
3533
+ form.append("file", asBlob(file), uploadName(file, opts.filename));
3490
3534
  form.append("tags", (opts.tags ?? []).join(","));
3491
3535
  if (opts.visibility)
3492
3536
  form.append("visibility", opts.visibility);