@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/cjs/index.js CHANGED
@@ -773,6 +773,25 @@ function asBlob(input) {
773
773
  // handing that straight to Blob can carry bytes belonging to something else.
774
774
  return new Blob([bytes.slice()]);
775
775
  }
776
+ /**
777
+ * The name a document is stored under: what the caller passed, the file's own, or a
778
+ * placeholder.
779
+ *
780
+ * One function for both clients, because they disagreed about it. The project client
781
+ * derived a name and fell back to `"upload.bin"`; the end-user client took `filename` in a
782
+ * **required** options object, so a JavaScript caller writing the documented
783
+ * `uploadAndWait(file)` got `TypeError: Cannot read properties of undefined (reading
784
+ * 'filename')` — thrown from inside the SDK, with nothing in the stack naming Oberik, at
785
+ * the reader most likely to have copied the snippet (OBE-195).
786
+ *
787
+ * Both are optional now and both land here. `"upload.bin"` rather than a refusal: a `File`
788
+ * carries its name and a Blob, Buffer or ArrayBuffer does not, and an upload that arrives
789
+ * with a placeholder name is visibly wrong in the listing, where a caller can fix it with
790
+ * `documents.update`. Pass `filename` — it is what the document is listed and cited under.
791
+ */
792
+ function uploadName(file, filename) {
793
+ return filename || file?.name || "upload.bin";
794
+ }
776
795
  /** Normalise whatever an `onQuestion` handler returned into an answer the API accepts.
777
796
  *
778
797
  * A handler returning a bare string was silently ignored. `{ ...answer }` on a string
@@ -2378,12 +2397,18 @@ class AgentFramework {
2378
2397
  delete: (id) => this.request("DELETE", `/documents/${id}`),
2379
2398
  retrieve: (body) => this.request("POST", "/documents/retrieve", { body }),
2380
2399
  /** Small-file convenience upload via multipart form (server proxies to S3). */
2381
- uploadSimple: async (file, opts) => {
2400
+ uploadSimple: async (file,
2401
+ // Optional, and the same shape the project client takes: this signature required an
2402
+ // options object, so `uploadSimple(file)` and `uploadAndWait(file)` — the way the
2403
+ // docs write them — threw a TypeError out of the SDK's own body instead of saying
2404
+ // what was missing (OBE-195).
2405
+ opts = {}) => {
2406
+ const filename = uploadName(file, opts.filename);
2382
2407
  const form = new FormData();
2383
2408
  const blob = typeof Blob !== "undefined" && file instanceof Blob
2384
2409
  ? file
2385
2410
  : new Blob([file], { type: opts.contentType ?? "application/octet-stream" });
2386
- form.append("file", blob, opts.filename);
2411
+ form.append("file", blob, filename);
2387
2412
  form.append("tags", (opts.tags ?? []).join(","));
2388
2413
  // Set visibility AT UPLOAD, not after: a document is retrievable as soon as
2389
2414
  // ingestion finishes, so patching later leaves a window where it's readable by
@@ -2416,7 +2441,7 @@ class AgentFramework {
2416
2441
  return (await res.json());
2417
2442
  },
2418
2443
  /** Resumable presigned multipart upload (direct to S3). */
2419
- upload: (file, opts) => this.multipartUpload(file, opts),
2444
+ upload: (file, opts = {}) => this.multipartUpload(file, opts),
2420
2445
  /** Poll a document until ingestion finishes (status "ready" or "failed").
2421
2446
  * Ingestion is async, so querying a just-uploaded doc may return nothing until
2422
2447
  * this resolves. Throws on "failed" or timeout. */
@@ -2440,7 +2465,7 @@ class AgentFramework {
2440
2465
  // Takes the ACL fields too: this is the upload most callers use, so omitting them
2441
2466
  // here forced a second `patch` and left a window where the document was already
2442
2467
  // retrievable at its default visibility.
2443
- opts) => {
2468
+ opts = {}) => {
2444
2469
  const doc = await this.documents.uploadSimple(file, opts);
2445
2470
  return this.documents.waitReady(doc.id, { timeoutMs: opts.timeoutMs, signal: opts.signal });
2446
2471
  },
@@ -3101,7 +3126,7 @@ class AgentFramework {
3101
3126
  const maxRetries = opts.maxRetries ?? 5;
3102
3127
  const presign = await this.request("POST", "/documents/presign-upload", {
3103
3128
  body: {
3104
- filename: opts.filename,
3129
+ filename: uploadName(file, opts.filename),
3105
3130
  content_type: opts.contentType,
3106
3131
  tags: opts.tags ?? [],
3107
3132
  ...(opts.visibility ? { visibility: opts.visibility } : {}),
@@ -3381,6 +3406,9 @@ class OberikProject {
3381
3406
  * turning citation markers off, meant dropping to raw HTTP through `raw()`.
3382
3407
  */
3383
3408
  project = {
3409
+ /** The project, its capability ceiling and its settings — plus `readiness` and the
3410
+ * `health` / `healthDetail` badge, so "is this project working" is answered by the
3411
+ * first thing you fetch rather than one route further on. */
3384
3412
  get: () => this.request("GET", ""),
3385
3413
  /** Merges — send only what you want to change. */
3386
3414
  set: (fields) => this.request("PATCH", "", fields),
@@ -3454,9 +3482,26 @@ class OberikProject {
3454
3482
  * accepted nor needed — a recipe that worked by luck rather than by expression.
3455
3483
  */
3456
3484
  documents = {
3457
- /** Same filters as the end-user client's `documents.list`, so the two agree. */
3485
+ /**
3486
+ * One PAGE of the corpus — the same envelope, the same filters and the same paging as
3487
+ * the end-user client's `documents.list`, because they are the same route.
3488
+ *
3489
+ * The route became a page in OBE-190 and this declaration did not move with it: it
3490
+ * still said `Promise<DocumentOut[]>`, so a TypeScript caller wrote `.length` and got
3491
+ * `undefined` with no type error and no exception — a census that counted documents
3492
+ * reported zero. That is OBE-147 verbatim, one collection over, and the docstring
3493
+ * describing it sits sixty lines above this one (OBE-195). The query type had the
3494
+ * mirror-image gap: `limit`/`offset` worked at runtime and were unwritable in
3495
+ * TypeScript, so the documented paging loop did not compile against the client that
3496
+ * recommends it.
3497
+ *
3498
+ * `with_total` adds a `total`; it is what "how big is this corpus" is asked with, and
3499
+ * it costs a second query. "Is there another page" is `has_more`, and it is free.
3500
+ */
3458
3501
  list: (query = {}) => {
3459
- const qs = new URLSearchParams(Object.entries(query).filter(([, v]) => v != null)).toString();
3502
+ const qs = new URLSearchParams(Object.entries(query)
3503
+ .filter(([, v]) => v != null)
3504
+ .map(([k, v]) => [k, String(v)])).toString();
3460
3505
  return this.request("GET", `/documents${qs ? `?${qs}` : ""}`);
3461
3506
  },
3462
3507
  get: (documentId) => this.request("GET", `/documents/${documentId}`),
@@ -3501,8 +3546,7 @@ class OberikProject {
3501
3546
  */
3502
3547
  opts = {}) => {
3503
3548
  const form = new FormData();
3504
- const name = opts.filename ?? file.name ?? "upload.bin";
3505
- form.append("file", asBlob(file), name);
3549
+ form.append("file", asBlob(file), uploadName(file, opts.filename));
3506
3550
  form.append("tags", (opts.tags ?? []).join(","));
3507
3551
  if (opts.visibility)
3508
3552
  form.append("visibility", opts.visibility);