bunderstack 0.25.1 → 0.25.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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to `bunderstack` will be documented in this file.
4
4
 
5
+ ## [0.25.2] — 2026-09-27
6
+
7
+ ### Fixed
8
+
9
+ - `files.<bucket>.upload(file)` from the query client no longer fails with
10
+ `NOT_FOUND` for buckets that use presigned uploads (S3, Tigris). The client
11
+ sent the full file id (`media/<uuid>.jpg`) to `confirmUpload`, and the server
12
+ added the bucket name again. The client now sends the bucket-relative id.
13
+ Proxy uploads (local storage) were not affected.
14
+
5
15
  ## [0.25.1] — 2026-09-25
6
16
 
7
17
  ### Fixed
@@ -70,7 +70,7 @@ function attachFileHelpers(utils, options) {
70
70
  throw new Error(`File upload failed (${uploaded.status})`);
71
71
  }
72
72
  const result = await procedures.confirmUpload.call({
73
- id: prepared.fileId,
73
+ id: relativeId(property, prepared.fileId),
74
74
  });
75
75
  return { ...result, url: url(result.fileId), name: file.name };
76
76
  };
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/query/client.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EAAE,WAAW,EAAuB,MAAM,SAAS,CAAA;AAmC1D,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AACjC,CAAC;AAED,SAAS,UAAU,CAAC,MAAc,EAAE,UAAkB;IACpD,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,CAAA;IAC3B,OAAO,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC;QAClC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;QACjC,CAAC,CAAC,UAAU,CAAA;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,kBAAkB,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CACtE;SACA,IAAI,CAAC,GAAG,CAAC,CAAA;AACd,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAQ,EACR,OAAsB;IAEtB,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACxC,MAAM,KAAK,GAAI,KAAiC,CAAC,OAAO,CAE3C,CAAA;IACb,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAExB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAA;IAC1C,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE;QAClC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACpC,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAA;YAEzB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAGxD,CAAA;YACD,MAAM,IAAI,GAAG,GAAG,OAAO,UAAU,QAAQ,EAAE,CAAA;YAC3C,MAAM,GAAG,GAAG,CACV,UAAkB,EAClB,aAAmC,EAAE,EACrC,EAAE;gBACF,MAAM,IAAI,GAAG,cAAc,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAA;gBAC7D,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;gBACpC,IAAI,UAAU,CAAC,CAAC,KAAK,SAAS;oBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;gBACrE,IAAI,UAAU,CAAC,CAAC,KAAK,SAAS;oBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;gBACrE,IAAI,UAAU,CAAC,MAAM;oBAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;gBAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;gBAC/B,OAAO,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;YACrD,CAAC,CAAA;YACD,MAAM,MAAM,GAAG,KAAK,EAAE,IAAU,EAAyB,EAAE;gBACzD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,aAAc,CAAC,IAAI,CAAC;oBACpD,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,WAAW,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;iBACpC,CAAC,CAAA;gBACF,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC9B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAO,CAAC,IAAI,CAAC;wBAC3C,MAAM,EAAE,EAAE;wBACV,KAAK,EAAE,EAAE;wBACT,OAAO,EAAE,EAAE;wBACX,IAAI,EAAE,EAAE,IAAI,EAAE;qBACf,CAAC,CAAA;oBACF,OAAO;wBACL,GAAG,MAAM,CAAC,IAAI;wBACd,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;wBAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;qBAChB,CAAA;gBACH,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE;oBAC/C,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;iBAC/D,CAAC,CAAA;gBACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;gBAC5D,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,aAAc,CAAC,IAAI,CAAC;oBAClD,EAAE,EAAE,QAAQ,CAAC,MAAM;iBACpB,CAAC,CAAA;gBACF,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;YAChE,CAAC,CAAA;YAED,MAAM,UAAU,GAAG,CAAC,UAAkB,EAAE,EAAE,CACxC,UAAU,CAAC,MAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;YAErE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE;gBACtD,GAAG;gBACH,MAAM;gBACN,MAAM,EAAE,UAAU;aACnB,CAAC,CAAA;YACF,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YAC7B,OAAO,MAAM,CAAA;QACf,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE;QACtB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,OAAO,QAAQ,KAAK,OAAO;gBACzB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAC7C,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,UAAyB,EAAE;IAE3B,OAAO,iBAAiB,CACtB,eAAe,CAAuB,OAAO,CAAC,EAC9C,OAAO,CACmB,CAAA;AAC9B,CAAC","sourcesContent":["import type { QueryClient } from '@tanstack/query-core'\n\nimport type { ApiQueryUtils } from './api'\nimport type { AnyBunderstackApp, InferApiRouter, InferBuckets } from './infer'\n\nimport { createApiClient } from './api'\nimport { createFetch, type TransportFetch } from './fetch'\n\nexport type ClientOptions = {\n baseUrl?: string\n fetch?: TransportFetch\n queryClient?: QueryClient\n}\n\nexport type FileTransformOptions = {\n w?: number\n h?: number\n format?: 'webp' | 'jpeg' | 'png' | 'avif'\n}\n\nexport type UploadedFile = {\n fileId: string\n url: string\n name: string\n}\n\nexport type FileBucketHelpers = {\n url(idOrFileId: string, transforms?: FileTransformOptions): string\n upload(file: File): Promise<UploadedFile>\n delete(idOrFileId: string): Promise<void>\n}\n\ntype FileHelpers<TBuckets extends string> = {\n files: { [K in TBuckets]: FileBucketHelpers }\n}\n\nexport type BunderstackClient<TApp extends AnyBunderstackApp> = ApiQueryUtils<\n InferApiRouter<TApp>\n> &\n FileHelpers<InferBuckets<TApp>>\n\nfunction trimSlash(value: string): string {\n return value.replace(/\\/$/, '')\n}\n\nfunction relativeId(bucket: string, idOrFileId: string): string {\n const prefix = `${bucket}/`\n return idOrFileId.startsWith(prefix)\n ? idOrFileId.slice(prefix.length)\n : idOrFileId\n}\n\nfunction encodeFilePath(value: string): string {\n return value\n .split('/')\n .map((segment) =>\n encodeURIComponent(decodeURIComponent(segment)).replace(/\\./g, '%2E'),\n )\n .join('/')\n}\n\nfunction attachFileHelpers<T extends object>(\n utils: T,\n options: ClientOptions,\n): T {\n const baseUrl = trimSlash(options.baseUrl ?? '/api')\n const fetch = createFetch(options.fetch)\n const files = (utils as Record<string, unknown>)['files'] as\n | Record<string, unknown>\n | undefined\n if (!files) return utils\n\n const buckets = new Map<string, unknown>()\n const filesProxy = new Proxy(files, {\n get(target, property, receiver) {\n if (typeof property !== 'string')\n return Reflect.get(target, property, receiver)\n const cached = buckets.get(property)\n if (cached) return cached\n\n const procedures = Reflect.get(target, property, receiver) as Record<\n string,\n { call(input: unknown): Promise<any> }\n >\n const root = `${baseUrl}/files/${property}`\n const url = (\n idOrFileId: string,\n transforms: FileTransformOptions = {},\n ) => {\n const path = encodeFilePath(relativeId(property, idOrFileId))\n const params = new URLSearchParams()\n if (transforms.w !== undefined) params.set('w', String(transforms.w))\n if (transforms.h !== undefined) params.set('h', String(transforms.h))\n if (transforms.format) params.set('format', transforms.format)\n const query = params.toString()\n return `${root}/${path}${query ? `?${query}` : ''}`\n }\n const upload = async (file: File): Promise<UploadedFile> => {\n const prepared = await procedures.prepareUpload!.call({\n filename: file.name,\n contentType: file.type || undefined,\n })\n if (prepared.mode === 'proxy') {\n const result = await procedures.upload!.call({\n params: {},\n query: {},\n headers: {},\n body: { file },\n })\n return {\n ...result.body,\n url: url(result.body.fileId),\n name: file.name,\n }\n }\n\n const uploaded = await fetch(prepared.uploadUrl, {\n method: prepared.method,\n body: file,\n headers: file.type ? { 'Content-Type': file.type } : undefined,\n })\n if (!uploaded.ok) {\n throw new Error(`File upload failed (${uploaded.status})`)\n }\n const result = await procedures.confirmUpload!.call({\n id: prepared.fileId,\n })\n return { ...result, url: url(result.fileId), name: file.name }\n }\n\n const deleteFile = (idOrFileId: string) =>\n procedures.delete!.call({ path: relativeId(property, idOrFileId) })\n\n const bucket = Object.assign(Object.create(procedures), {\n url,\n upload,\n delete: deleteFile,\n })\n buckets.set(property, bucket)\n return bucket\n },\n })\n\n return new Proxy(utils, {\n get(target, property, receiver) {\n return property === 'files'\n ? filesProxy\n : Reflect.get(target, property, receiver)\n },\n })\n}\n\nexport function createClient<TApp extends AnyBunderstackApp>(\n options: ClientOptions = {},\n): BunderstackClient<TApp> {\n return attachFileHelpers(\n createApiClient<InferApiRouter<TApp>>(options),\n options,\n ) as BunderstackClient<TApp>\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/query/client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { QueryClient } from '@tanstack/query-core'\n\nimport type { ApiQueryUtils } from './api'\nimport type { AnyBunderstackApp, InferApiRouter, InferBuckets } from './infer'\n\nimport { createApiClient } from './api'\nimport { createFetch, type TransportFetch } from './fetch'\n\nexport type ClientOptions = {\n baseUrl?: string\n fetch?: TransportFetch\n queryClient?: QueryClient\n}\n\nexport type FileTransformOptions = {\n w?: number\n h?: number\n format?: 'webp' | 'jpeg' | 'png' | 'avif'\n}\n\nexport type UploadedFile = {\n fileId: string\n url: string\n name: string\n}\n\nexport type FileBucketHelpers = {\n url(idOrFileId: string, transforms?: FileTransformOptions): string\n upload(file: File): Promise<UploadedFile>\n delete(idOrFileId: string): Promise<void>\n}\n\ntype FileHelpers<TBuckets extends string> = {\n files: { [K in TBuckets]: FileBucketHelpers }\n}\n\nexport type BunderstackClient<TApp extends AnyBunderstackApp> = ApiQueryUtils<\n InferApiRouter<TApp>\n> &\n FileHelpers<InferBuckets<TApp>>\n\nfunction trimSlash(value: string): string {\n return value.replace(/\\/$/, '')\n}\n\nfunction relativeId(bucket: string, idOrFileId: string): string {\n const prefix = `${bucket}/`\n return idOrFileId.startsWith(prefix)\n ? idOrFileId.slice(prefix.length)\n : idOrFileId\n}\n\nfunction encodeFilePath(value: string): string {\n return value\n .split('/')\n .map((segment) =>\n encodeURIComponent(decodeURIComponent(segment)).replace(/\\./g, '%2E'),\n )\n .join('/')\n}\n\nfunction attachFileHelpers<T extends object>(\n utils: T,\n options: ClientOptions,\n): T {\n const baseUrl = trimSlash(options.baseUrl ?? '/api')\n const fetch = createFetch(options.fetch)\n const files = (utils as Record<string, unknown>)['files'] as\n | Record<string, unknown>\n | undefined\n if (!files) return utils\n\n const buckets = new Map<string, unknown>()\n const filesProxy = new Proxy(files, {\n get(target, property, receiver) {\n if (typeof property !== 'string')\n return Reflect.get(target, property, receiver)\n const cached = buckets.get(property)\n if (cached) return cached\n\n const procedures = Reflect.get(target, property, receiver) as Record<\n string,\n { call(input: unknown): Promise<any> }\n >\n const root = `${baseUrl}/files/${property}`\n const url = (\n idOrFileId: string,\n transforms: FileTransformOptions = {},\n ) => {\n const path = encodeFilePath(relativeId(property, idOrFileId))\n const params = new URLSearchParams()\n if (transforms.w !== undefined) params.set('w', String(transforms.w))\n if (transforms.h !== undefined) params.set('h', String(transforms.h))\n if (transforms.format) params.set('format', transforms.format)\n const query = params.toString()\n return `${root}/${path}${query ? `?${query}` : ''}`\n }\n const upload = async (file: File): Promise<UploadedFile> => {\n const prepared = await procedures.prepareUpload!.call({\n filename: file.name,\n contentType: file.type || undefined,\n })\n if (prepared.mode === 'proxy') {\n const result = await procedures.upload!.call({\n params: {},\n query: {},\n headers: {},\n body: { file },\n })\n return {\n ...result.body,\n url: url(result.body.fileId),\n name: file.name,\n }\n }\n\n const uploaded = await fetch(prepared.uploadUrl, {\n method: prepared.method,\n body: file,\n headers: file.type ? { 'Content-Type': file.type } : undefined,\n })\n if (!uploaded.ok) {\n throw new Error(`File upload failed (${uploaded.status})`)\n }\n const result = await procedures.confirmUpload!.call({\n id: relativeId(property, prepared.fileId),\n })\n return { ...result, url: url(result.fileId), name: file.name }\n }\n\n const deleteFile = (idOrFileId: string) =>\n procedures.delete!.call({ path: relativeId(property, idOrFileId) })\n\n const bucket = Object.assign(Object.create(procedures), {\n url,\n upload,\n delete: deleteFile,\n })\n buckets.set(property, bucket)\n return bucket\n },\n })\n\n return new Proxy(utils, {\n get(target, property, receiver) {\n return property === 'files'\n ? filesProxy\n : Reflect.get(target, property, receiver)\n },\n })\n}\n\nexport function createClient<TApp extends AnyBunderstackApp>(\n options: ClientOptions = {},\n): BunderstackClient<TApp> {\n return attachFileHelpers(\n createApiClient<InferApiRouter<TApp>>(options),\n options,\n ) as BunderstackClient<TApp>\n}\n"]}
package/llms-full.txt CHANGED
@@ -740,7 +740,16 @@ latest captured verification link for that user's email.
740
740
  ## Required schema tables
741
741
 
742
742
  Include `user`, `session`, `account`, `verification` from BetterAuth in your schema.
743
- Copy full definitions from `examples/standalone/schema.ts`.
743
+ Generate them with `npx auth generate`, or copy the definitions from the
744
+ [SaaS template](https://github.com/kirill-dev-pro/bunderstack/blob/main/templates/tanstack-start-saas/src/bunderstack/schema/auth.ts).
745
+
746
+ Better Auth 1.7.6 and later check these tables when the app starts and before
747
+ every auth call. A missing table or column, or a required column that Better
748
+ Auth never writes, fails the request with `Drizzle schema mismatch`. Apps
749
+ created with Better Auth 1.7.0–1.7.2 may still have a required
750
+ `account.issuer` column; follow the
751
+ [1.7 upgrade guide](https://www.better-auth.com/docs/guides/1-7-upgrade-guide)
752
+ before removing it.
744
753
 
745
754
  An app whose schema has no `user` and `session` tables runs without Better Auth:
746
755
  every session resolves to `null`, `/api/auth/*` answers 404, and the OpenAPI
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunderstack",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "description": "Batteries-included backend framework for Bun: type-safe oRPC APIs, auth, storage, realtime, jobs, messaging, and validated env from one declaration.",
5
5
  "keywords": [
6
6
  "backend",