@bunbase-ae/js 2.13.3-next.300.4a69028 → 2.13.3-next.304.484953e

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunbase-ae/js",
3
- "version": "2.13.3-next.300.4a69028",
3
+ "version": "2.13.3-next.304.484953e",
4
4
  "type": "module",
5
5
  "description": "TypeScript/JavaScript SDK for BunBase",
6
6
  "license": "UNLICENSED",
package/src/admin.ts CHANGED
@@ -650,23 +650,25 @@ class AdminCollectionsClient {
650
650
  if (opts.includeDeleted) params.include_deleted = "true";
651
651
  if (opts.search) params.search = opts.search;
652
652
  if (opts.filter) {
653
+ // Encode filters as a single ?filter=[{field,op,value}, …] JSON array.
654
+ // See buildQueryString in collection.ts for the rationale (issue #533):
655
+ // the bracket form silently corrupts values containing commas.
656
+ const fieldFilters: { field: string; op: string; value: unknown }[] = [];
653
657
  for (const [key, value] of Object.entries(opts.filter)) {
654
658
  if (value === undefined || value === null) continue;
655
- if (typeof value === "object" && !Array.isArray(value)) {
656
- // Operator filter: { age: { gte: 18 } } → filter[age][gte]=18
659
+ if (Array.isArray(value)) {
660
+ fieldFilters.push({ field: key, op: "in", value });
661
+ } else if (typeof value === "object") {
657
662
  for (const [op, opVal] of Object.entries(value as Record<string, unknown>)) {
658
663
  if (opVal !== undefined && opVal !== null) {
659
- params[`filter[${key}][${op}]`] = String(opVal);
664
+ fieldFilters.push({ field: key, op, value: opVal });
660
665
  }
661
666
  }
662
- } else if (Array.isArray(value)) {
663
- // Array shorthand: { stage: ["a", "b"] } → filter[stage][in]=a,b
664
- params[`filter[${key}][in]`] = value.join(",");
665
667
  } else {
666
- // Equality filter: { status: "active" } → filter[status]=active
667
- params[`filter[${key}]`] = String(value);
668
+ fieldFilters.push({ field: key, op: "eq", value });
668
669
  }
669
670
  }
671
+ if (fieldFilters.length) params.filter = JSON.stringify(fieldFilters);
670
672
  }
671
673
  return this.http.request<AdminListResult<T & AdminRecord>>(
672
674
  "GET",
package/src/collection.ts CHANGED
@@ -161,24 +161,28 @@ export function buildQueryString<T extends Record<string, unknown> = Record<stri
161
161
  const params: Record<string, string> = {};
162
162
 
163
163
  if (query.filter) {
164
+ // Encode filters as a single ?filter=[{field,op,value}, …] JSON array.
165
+ // The bracket form (?filter[field][op]=value) joins array values on ",", which
166
+ // silently corrupts any value containing a comma — see issue #533. The JSON
167
+ // form preserves array values verbatim and is parsed by the server's
168
+ // parseFilterJson (apps/server/src/routes/_filterParams.ts).
169
+ const fieldFilters: { field: string; op: string; value: unknown }[] = [];
164
170
  for (const [field, value] of Object.entries(query.filter)) {
165
171
  if (value === undefined || value === null) continue;
166
172
 
167
- if (typeof value === "object" && !Array.isArray(value)) {
168
- // Operator filter: { age: { gte: 18 } } → filter[age][gte]=18
173
+ if (Array.isArray(value)) {
174
+ fieldFilters.push({ field, op: "in", value });
175
+ } else if (typeof value === "object") {
169
176
  for (const [op, opVal] of Object.entries(value as Record<string, unknown>)) {
170
177
  if (opVal !== undefined && opVal !== null) {
171
- params[`filter[${field}][${op}]`] = String(opVal);
178
+ fieldFilters.push({ field, op, value: opVal });
172
179
  }
173
180
  }
174
- } else if (Array.isArray(value)) {
175
- // Array shorthand: { stage: ["a", "b"] } → filter[stage][in]=a,b
176
- params[`filter[${field}][in]`] = value.join(",");
177
181
  } else {
178
- // Equality filter: { status: "published" } → filter[status]=published
179
- params[`filter[${field}]`] = String(value);
182
+ fieldFilters.push({ field, op: "eq", value });
180
183
  }
181
184
  }
185
+ if (fieldFilters.length) params.filter = JSON.stringify(fieldFilters);
182
186
  }
183
187
 
184
188
  if (query.sort) params.sort = query.sort;
package/src/types.ts CHANGED
@@ -166,8 +166,10 @@ export type FilterFieldValue<V> = V | FilterArrayShorthandValue<V> | FilterOpera
166
166
  * filter: { rfid_tag: { in: ["a", "b"] } }
167
167
  * filter: { score: { gte: 0, lte: 100 } }
168
168
  */
169
- // Simple: { status: "published" } → filter[status]=published
170
- // Operator: { age: { gte: 18 } } → filter[age][gte]=18
169
+ // Wire format: a single ?filter=[{field,op,value}, …] JSON entry.
170
+ // Simple { status: "published" } → [{ field: "status", op: "eq", value: "published" }]
171
+ // Operator { age: { gte: 18 } } → [{ field: "age", op: "gte", value: 18 }]
172
+ // Array { id: ["a", "b"] } → [{ field: "id", op: "in", value: ["a", "b"] }]
171
173
  export type Filter<T = Record<string, unknown>> = {
172
174
  [K in Exclude<keyof T, keyof BunBaseRecord>]?: FilterFieldValue<T[K]>;
173
175
  } & {