@mohasinac/appkit 4.9.3 → 4.9.4

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.
@@ -191,7 +191,18 @@ const TOKEN_TO_OP = {
191
191
  ">=": ">=",
192
192
  "@=": "array-contains",
193
193
  };
194
- /** Best-effort scalar coercion for sieve filter values. */
194
+ /** Matches a bare `YYYY-MM-DD` date or a full ISO-8601 timestamp. */
195
+ const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$/;
196
+ /**
197
+ * Best-effort scalar coercion for sieve filter values, used by the legacy
198
+ * `findAll()` query path (no `SieveFieldConfig`/`parseValue` mechanism
199
+ * exists here — see `./sieve.ts`'s `sieveQuery`/`applySieveToFirestore` for
200
+ * that). An ISO-date-shaped string is converted to a real `Date` so a range
201
+ * filter (`>`/`<`/`>=`/`<=`) on a Firestore Timestamp field compares
202
+ * correctly — without this, the string reaches `.where(field, op, value)`
203
+ * unchanged and Firestore's type-ordering silently matches ZERO documents
204
+ * (CLAUDE.md Recurrent Root Cause Pattern #47).
205
+ */
195
206
  function coerceValue(raw) {
196
207
  if (raw === "true")
197
208
  return true;
@@ -199,6 +210,11 @@ function coerceValue(raw) {
199
210
  return false;
200
211
  if (raw === "null")
201
212
  return null;
213
+ if (ISO_DATE_RE.test(raw)) {
214
+ const d = new Date(raw);
215
+ if (!Number.isNaN(d.getTime()))
216
+ return d;
217
+ }
202
218
  const n = Number(raw);
203
219
  if (!Number.isNaN(n) && raw !== "")
204
220
  return n;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.9.3",
3
+ "version": "4.9.4",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"