Skip to content

fix(cloudflare): D1 value-union params + Vectorize raw x-ndjson body#379

Merged
sam-goodwin merged 4 commits into
mainfrom
claude/d1-vectorize-local-fixes
Jul 16, 2026
Merged

fix(cloudflare): D1 value-union params + Vectorize raw x-ndjson body#379
sam-goodwin merged 4 commits into
mainfrom
claude/d1-vectorize-local-fixes

Conversation

@sam-goodwin

@sam-goodwin sam-goodwin commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Three Cloudflare SDK-accuracy fixes surfaced by the alchemy *Local bindings work (alchemy-run/alchemy#807), plus the small core/generator enablers they need.

D1 — params typed as string[]

The cloudflare-typescript SDK genuinely under-types D1 query/raw params (single and batch) as Array<string>, so Distilled rejected native D1 bind values during request encoding (SchemaError: Expected string, got 42). The D1 HTTP API accepts numbers/null/BLOB verbatim (verified live), so model params as the wire-accurate value union string | number | null | number[] (TEXT/INTEGER-REAL/NULL/BLOB) via request patches on queryDatabase + rawDatabase.

Vectorize — insert/upsert sent as multipart instead of a raw body

The SDK sends the ndjson payload as a raw application/x-ndjson request body:

this._client.post(url, { body, headers: { 'Content-Type': 'application/x-ndjson' } });

The generator's Core.Uploadable ⇒ multipart form file heuristic mis-modeled it. Model it as a raw binary body carrying application/x-ndjson so it matches the SDK/API (not multipart).

Core / generator enablers

  • HttpTrait.bodyMediaType — a contentType: "binary" request can now carry a specific Content-Type (e.g. application/x-ndjson) instead of the generic application/octet-stream. setBinaryBody threads it into the HttpBody so setBody can't clobber it; a caller-supplied content-type header still wins.
  • The cloudflare generator now treats a format: binary body under any non-multipart content-type as a raw binary body (not a form file) and carries its media type through.
  • A patch appendUnion now wraps a non-union scalar field into a union (previously it only extended existing unions), which is what makes the D1 value union expressible.

All regenerated against current main; the cloudflare package builds, and the changes are exercised by alchemy's live D1/Vectorize/R2 *Local tests (binary-body path unregressed).

@alchemy-version-bot

alchemy-version-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

@distilled.cloud/core

bun add @distilled.cloud/core@https://pkg.distilled.cloud/core/ef9c953

@distilled.cloud/aws

bun add @distilled.cloud/aws@https://pkg.distilled.cloud/aws/ef9c953

@distilled.cloud/axiom

bun add @distilled.cloud/axiom@https://pkg.distilled.cloud/axiom/ef9c953

@distilled.cloud/azure

bun add @distilled.cloud/azure@https://pkg.distilled.cloud/azure/ef9c953

@distilled.cloud/cloudflare

bun add @distilled.cloud/cloudflare@https://pkg.distilled.cloud/cloudflare/ef9c953

@distilled.cloud/coinbase

bun add @distilled.cloud/coinbase@https://pkg.distilled.cloud/coinbase/ef9c953

@distilled.cloud/expo-eas

bun add @distilled.cloud/expo-eas@https://pkg.distilled.cloud/expo-eas/ef9c953

@distilled.cloud/fly-io

bun add @distilled.cloud/fly-io@https://pkg.distilled.cloud/fly-io/ef9c953

@distilled.cloud/gcp

bun add @distilled.cloud/gcp@https://pkg.distilled.cloud/gcp/ef9c953

@distilled.cloud/kubernetes

bun add @distilled.cloud/kubernetes@https://pkg.distilled.cloud/kubernetes/ef9c953

@distilled.cloud/mongodb-atlas

bun add @distilled.cloud/mongodb-atlas@https://pkg.distilled.cloud/mongodb-atlas/ef9c953

@distilled.cloud/neon

bun add @distilled.cloud/neon@https://pkg.distilled.cloud/neon/ef9c953

@distilled.cloud/planetscale

bun add @distilled.cloud/planetscale@https://pkg.distilled.cloud/planetscale/ef9c953

@distilled.cloud/posthog

bun add @distilled.cloud/posthog@https://pkg.distilled.cloud/posthog/ef9c953

@distilled.cloud/prisma-postgres

bun add @distilled.cloud/prisma-postgres@https://pkg.distilled.cloud/prisma-postgres/ef9c953

@distilled.cloud/stripe

bun add @distilled.cloud/stripe@https://pkg.distilled.cloud/stripe/ef9c953

@distilled.cloud/supabase

bun add @distilled.cloud/supabase@https://pkg.distilled.cloud/supabase/ef9c953

@distilled.cloud/turso

bun add @distilled.cloud/turso@https://pkg.distilled.cloud/turso/ef9c953

@distilled.cloud/typesense

bun add @distilled.cloud/typesense@https://pkg.distilled.cloud/typesense/ef9c953

@distilled.cloud/workos

bun add @distilled.cloud/workos@https://pkg.distilled.cloud/workos/ef9c953

sam-goodwin added a commit to alchemy-run/alchemy that referenced this pull request Jul 15, 2026
Rebases the D1 params + Vectorize multipart fixes onto distilled main
(companion PR alchemy-run/distilled#379), dropping the stale
claude/typescript-7-stable base so the submodule pointer no longer
conflicts with alchemy main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sam-goodwin
sam-goodwin force-pushed the claude/d1-vectorize-local-fixes branch from 1103d29 to 7d06469 Compare July 15, 2026 20:20
sam-goodwin added a commit to alchemy-run/alchemy that referenced this pull request Jul 15, 2026
Repoints the submodule to distilled 7d06469fc (companion PR
alchemy-run/distilled#379): D1 params are now a precise value union
`string | number | null | number[]` instead of unknown[].

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tches

- HttpTrait.bodyMediaType lets a `contentType: "binary"` request carry a
  specific Content-Type (e.g. application/x-ndjson) instead of the generic
  application/octet-stream; setBinaryBody threads it into the HttpBody so
  `setBody` cannot clobber it. Caller content-type header still wins.
- cloudflare generator: a `format: binary` body under any non-multipart
  content-type is now a raw binary body (not a form-data file), and its
  media type is carried through as `bodyMediaType`.
- patch `appendUnion` now wraps a non-union scalar field into a union
  (previously it only extended existing unions).
…json body

The Cloudflare SDK sends the ndjson vector payload as a RAW request body
(Content-Type application/x-ndjson), but the generator was conflating the
Core.Uploadable body into a multipart form file. Model it as a raw binary
body with bodyMediaType application/x-ndjson so it matches the SDK/API.
The SDK types D1 params (single + batch) as string[], so Distilled rejected
native D1 bind values (.bind(42)/.bind(null)) during request encoding. Model
params as the wire-accurate value union string | number | null | number[]
(TEXT/INTEGER-REAL/NULL/BLOB) via request patches on queryDatabase +
rawDatabase.
@sam-goodwin
sam-goodwin force-pushed the claude/d1-vectorize-local-fixes branch from 7d06469 to 88349b7 Compare July 15, 2026 20:44
sam-goodwin added a commit to alchemy-run/alchemy that referenced this pull request Jul 15, 2026
Distilled now models vectorize insert/upsert as a raw application/x-ndjson
body (companion PR alchemy-run/distilled#379) matching the Cloudflare SDK,
instead of the multipart-with-vectors workaround. SearchIndexLocal passes
the ndjson blob as `body`. Bumps the distilled submodule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sam-goodwin sam-goodwin changed the title fix(cloudflare): D1 params unknown[] + Vectorize insert/upsert vectors part fix(cloudflare): D1 value-union params + Vectorize raw x-ndjson body Jul 15, 2026
@sam-goodwin
sam-goodwin merged commit 208876c into main Jul 16, 2026
35 of 43 checks passed
@sam-goodwin
sam-goodwin deleted the claude/d1-vectorize-local-fixes branch July 16, 2026 04:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant