strata-storage 2.7.0 → 2.7.1

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.
Files changed (37) hide show
  1. package/AI-INTEGRATION-GUIDE.md +41 -1
  2. package/README.md +1 -1
  3. package/dist/README.md +1 -1
  4. package/dist/adapters/web/CacheAdapter.d.ts.map +1 -1
  5. package/dist/adapters/web/CacheAdapter.js +20 -5
  6. package/dist/adapters/web/CookieAdapter.d.ts.map +1 -1
  7. package/dist/adapters/web/CookieAdapter.js +27 -3
  8. package/dist/adapters/web/IndexedDBAdapter.d.ts.map +1 -1
  9. package/dist/adapters/web/IndexedDBAdapter.js +5 -1
  10. package/dist/adapters/web/LocalStorageAdapter.d.ts.map +1 -1
  11. package/dist/adapters/web/LocalStorageAdapter.js +15 -1
  12. package/dist/adapters/web/MemoryAdapter.d.ts +7 -0
  13. package/dist/adapters/web/MemoryAdapter.d.ts.map +1 -1
  14. package/dist/adapters/web/MemoryAdapter.js +22 -5
  15. package/dist/adapters/web/SessionStorageAdapter.d.ts.map +1 -1
  16. package/dist/adapters/web/SessionStorageAdapter.js +14 -1
  17. package/dist/core/Strata.d.ts +12 -0
  18. package/dist/core/Strata.d.ts.map +1 -1
  19. package/dist/core/Strata.js +119 -21
  20. package/dist/features/compression.d.ts +23 -8
  21. package/dist/features/compression.d.ts.map +1 -1
  22. package/dist/features/compression.js +164 -77
  23. package/dist/features/migration.d.ts.map +1 -1
  24. package/dist/features/migration.js +7 -2
  25. package/dist/features/query.d.ts +1 -0
  26. package/dist/features/query.d.ts.map +1 -1
  27. package/dist/features/query.js +29 -6
  28. package/dist/features/sync.d.ts.map +1 -1
  29. package/dist/features/sync.js +18 -3
  30. package/dist/features/ttl.d.ts.map +1 -1
  31. package/dist/features/ttl.js +4 -2
  32. package/dist/firebase.d.ts.map +1 -1
  33. package/dist/firebase.js +39 -2
  34. package/dist/index.d.ts +2 -2
  35. package/dist/package.json +1 -1
  36. package/package.json +1 -1
  37. package/scripts/build.js +30 -13
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/features/query.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,SAAS,CAAC;AAG9D;;GAEG;AACH,qBAAa,WAAW;IACtB;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO;IAwB3D;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2DvB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB;;OAEG;IACH,OAAO,CAAC,MAAM;IA+Bd;;OAEG;IACH,OAAO,CAAC,OAAO;IAyBf;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,OAAO;IASf;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;IAoBxD;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA2B5E;;OAEG;IACH,OAAO,CAAC,cAAc;IAetB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAc1B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAE/C"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/features/query.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,SAAS,CAAC;AAG9D;;GAEG;AACH,qBAAa,WAAW;IACtB;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO;IAwB3D;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,eAAe;IAkEvB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB;;OAEG;IACH,OAAO,CAAC,MAAM;IA+Bd;;OAEG;IACH,OAAO,CAAC,OAAO;IAyBf;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,OAAO;IASf;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;IAoBxD;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA6B5E;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAsD;IAE5F,OAAO,CAAC,cAAc;IAyBtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAkB1B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAE/C"}
@@ -73,7 +73,15 @@ export class QueryEngine {
73
73
  // Otherwise check if value equals any operand value
74
74
  return operand.some((v) => this.equals(value, v));
75
75
  case '$nin':
76
- return Array.isArray(operand) && !operand.some((v) => this.equals(value, v));
76
+ if (!Array.isArray(operand))
77
+ return false;
78
+ // Mirror $in: for an array field, "not in" means none of its items
79
+ // match any operand value (the previous code compared the whole array
80
+ // to each scalar operand, giving wrong results for array fields).
81
+ if (Array.isArray(value)) {
82
+ return !operand.some((v) => value.some((item) => this.equals(item, v)));
83
+ }
84
+ return !operand.some((v) => this.equals(value, v));
77
85
  case '$regex':
78
86
  return this.matchesRegex(value, operand);
79
87
  case '$exists':
@@ -272,8 +280,10 @@ export class QueryEngine {
272
280
  }
273
281
  }
274
282
  else {
275
- // Exclusion mode - include all except specified fields
276
- result.value = { ...item };
283
+ // Exclusion mode - include all except specified fields. Spread the item's
284
+ // own fields directly onto result (NOT under a `value` wrapper, which left
285
+ // the excluded keys unreachable and returned the whole item unchanged).
286
+ Object.assign(result, item);
277
287
  for (const [key, exclude] of Object.entries(projection)) {
278
288
  if (exclude === 0 || exclude === false) {
279
289
  this.deleteNestedValue(result, key);
@@ -285,17 +295,28 @@ export class QueryEngine {
285
295
  /**
286
296
  * Set nested value in object using dot notation
287
297
  */
298
+ static FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
288
299
  setNestedValue(obj, path, value) {
289
300
  const parts = path.split('.');
290
301
  let current = obj;
291
302
  for (let i = 0; i < parts.length - 1; i++) {
292
303
  const part = parts[i];
293
- if (!(part in current) || typeof current[part] !== 'object') {
304
+ // Refuse prototype-polluting paths and use own-property checks only — `in`
305
+ // walks the prototype chain, so a '__proto__.x' path could reach
306
+ // Object.prototype.
307
+ if (QueryEngine.FORBIDDEN_KEYS.has(part))
308
+ return;
309
+ if (!Object.prototype.hasOwnProperty.call(current, part) ||
310
+ typeof current[part] !== 'object' ||
311
+ current[part] === null) {
294
312
  current[part] = {};
295
313
  }
296
314
  current = current[part];
297
315
  }
298
- current[parts[parts.length - 1]] = value;
316
+ const last = parts[parts.length - 1];
317
+ if (QueryEngine.FORBIDDEN_KEYS.has(last))
318
+ return;
319
+ current[last] = value;
299
320
  }
300
321
  /**
301
322
  * Delete nested value from object using dot notation
@@ -305,7 +326,9 @@ export class QueryEngine {
305
326
  let current = obj;
306
327
  for (let i = 0; i < parts.length - 1; i++) {
307
328
  const part = parts[i];
308
- if (!(part in current) || typeof current[part] !== 'object') {
329
+ if (!Object.prototype.hasOwnProperty.call(current, part) ||
330
+ typeof current[part] !== 'object' ||
331
+ current[part] === null) {
309
332
  return;
310
333
  }
311
334
  current = current[part];
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/features/sync.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAEV,WAAW,EAEX,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAYjB;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAC,CAAmB;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuE;IACxF,OAAO,CAAC,cAAc,CAAyD;gBAEnE,MAAM,GAAE,UAAe;IAYnC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,IAAI;IAuBrD;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,mBAAmB;IAY9D;;OAEG;IACH,KAAK,IAAI,IAAI;IAuBb;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAInC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;IA2C1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO;CA2C5C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,WAAW,CAElE"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/features/sync.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAEV,WAAW,EAEX,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAYjB;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAC,CAAmB;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuE;IACxF,OAAO,CAAC,cAAc,CAAyD;gBAEnE,MAAM,GAAE,UAAe;IAYnC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,IAAI;IAuBrD;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,mBAAmB;IAY9D;;OAEG;IACH,KAAK,IAAI,IAAI;IAuBb;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAInC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;IA2C1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO;CAyD5C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,WAAW,CAElE"}
@@ -247,9 +247,24 @@ export class SyncManager extends EventEmitter {
247
247
  return this.config.conflictResolution(values);
248
248
  }
249
249
  switch (this.config.conflictResolution) {
250
- case 'latest':
251
- // Return the last value (most recent)
252
- return values[values.length - 1];
250
+ case 'latest': {
251
+ // Most-recent-wins by the StorageValue `updated` timestamp so two tabs
252
+ // with concurrent same-key writes converge on the genuinely newest
253
+ // value, instead of each applying the other's (last-received-wins, which
254
+ // diverges and lets a delayed older message clobber a newer local
255
+ // write). Falls back to positional-last when values carry no timestamp.
256
+ let newest = values[values.length - 1];
257
+ let newestTime = isStorageValue(newest) ? newest.updated : undefined;
258
+ for (const candidate of values) {
259
+ if (!isStorageValue(candidate))
260
+ continue;
261
+ if (typeof newestTime !== 'number' || candidate.updated >= newestTime) {
262
+ newest = candidate;
263
+ newestTime = candidate.updated;
264
+ }
265
+ }
266
+ return newest;
267
+ }
253
268
  case 'merge':
254
269
  if (values.every(isStorageValue)) {
255
270
  const wrappers = values;
@@ -1 +1 @@
1
- {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../src/features/ttl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../src/features/ttl.ts"],"names":[],"mappings":"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"}
@@ -32,10 +32,12 @@ export class TTLManager extends EventEmitter {
32
32
  const time = options.expireAt instanceof Date ? options.expireAt.getTime() : options.expireAt;
33
33
  return time > Date.now() ? time : undefined;
34
34
  }
35
- // Expire after a certain date
35
+ // Expire after a certain date — documented as an alias of expireAt, so it
36
+ // gets the same past-guard (a past time yields no expiry rather than an
37
+ // entry that is dead on arrival).
36
38
  if (options.expireAfter) {
37
39
  const afterTime = options.expireAfter instanceof Date ? options.expireAfter.getTime() : options.expireAfter;
38
- return afterTime;
40
+ return afterTime > Date.now() ? afterTime : undefined;
39
41
  }
40
42
  // TTL from now
41
43
  if (options.ttl) {
@@ -1 +1 @@
1
- {"version":3,"file":"firebase.d.ts","sourceRoot":"","sources":["../src/firebase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+BD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAyOf;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAQ5D"}
1
+ {"version":3,"file":"firebase.d.ts","sourceRoot":"","sources":["../src/firebase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA4DD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAkPf;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAQ5D"}
package/dist/firebase.js CHANGED
@@ -6,6 +6,35 @@ function isStorageValue(value) {
6
6
  'created' in value &&
7
7
  'updated' in value);
8
8
  }
9
+ function isPlainObject(value) {
10
+ if (typeof value !== 'object' || value === null)
11
+ return false;
12
+ const proto = Object.getPrototypeOf(value);
13
+ return proto === Object.prototype || proto === null;
14
+ }
15
+ /**
16
+ * Recursively drop `undefined`-valued keys. Firestore `setDoc()` and Realtime
17
+ * Database `set()` both throw on `undefined` field values, and the StorageValue
18
+ * wrapper Strata builds always carries `expires`/`tags`/`metadata` keys that are
19
+ * `undefined` for a plain write — so writing it verbatim breaks the happy path.
20
+ * Only plain objects/arrays are traversed, so Dates and other class instances
21
+ * are preserved as-is.
22
+ */
23
+ function stripUndefined(input) {
24
+ if (Array.isArray(input)) {
25
+ return input.map((item) => stripUndefined(item));
26
+ }
27
+ if (isPlainObject(input)) {
28
+ const out = {};
29
+ for (const [key, val] of Object.entries(input)) {
30
+ if (val === undefined)
31
+ continue;
32
+ out[key] = stripUndefined(val);
33
+ }
34
+ return out;
35
+ }
36
+ return input;
37
+ }
9
38
  function normalizeRemoteValue(value) {
10
39
  if (!value)
11
40
  return null;
@@ -71,7 +100,7 @@ export async function enableFirebaseSync(storage, config) {
71
100
  },
72
101
  async set(key, value) {
73
102
  const docRef = doc(db, collectionName, key);
74
- await setDoc(docRef, value);
103
+ await setDoc(docRef, stripUndefined(value));
75
104
  },
76
105
  async remove(key) {
77
106
  const docRef = doc(db, collectionName, key);
@@ -168,7 +197,7 @@ export async function enableFirebaseSync(storage, config) {
168
197
  return snapshot.exists() ? normalizeRemoteValue(snapshot.val()) : null;
169
198
  },
170
199
  async set(key, value) {
171
- await set(ref(db, `strata-storage/${key}`), value);
200
+ await set(ref(db, `strata-storage/${key}`), stripUndefined(value));
172
201
  },
173
202
  async remove(key) {
174
203
  await remove(ref(db, `strata-storage/${key}`));
@@ -230,6 +259,14 @@ export async function enableFirebaseSync(storage, config) {
230
259
  // Cast to unknown first to bypass type checking for custom adapter
231
260
  storage.registerAdapter(realtimeAdapter);
232
261
  }
262
+ // Attach the freshly registered adapters to the active set so they join
263
+ // multi-adapter operations (subscribe/keys/clear/size) immediately — not
264
+ // only after an explicit `{ storage: 'firestore' }` call. Mirrors
265
+ // registerCapacitorAdapters(). Without this, a top-level subscribe() issued
266
+ // before any firestore op never wires up the onSnapshot listener.
267
+ if (config.firestore || config.realtimeDatabase) {
268
+ await storage.refreshAdapters();
269
+ }
233
270
  // Firebase sync enabled successfully
234
271
  }
235
272
  catch (error) {
package/dist/index.d.ts CHANGED
@@ -18,10 +18,10 @@ export { computeChecksum, verifyChecksum } from "./features/integrity.js";
18
18
  export { MigrationManager } from "./features/migration.js";
19
19
  export type { Migration } from "./features/migration.js";
20
20
  export { StrataError, StorageError, IntegrityError, QuotaExceededError, EncryptionError, CompressionError, SerializationError, ValidationError, NotSupportedError, AdapterNotAvailableError, } from "./utils/errors.js";
21
- export type { StorageType, StorageOptions, StorageValue, StorageAdapter, AdapterConfig, QueryOptions, SyncConfig, EncryptionConfig, CompressionConfig, ObserverCallback, StorageEvent, StorageCapabilities, StorageMetadata, TTLConfig, StrataConfig, StorageChange, SubscriptionCallback, UnsubscribeFunction, } from "./types.js";
21
+ export type { StorageType, StorageOptions, StorageValue, StorageAdapter, AdapterConfig, QueryOptions, SyncConfig, EncryptionConfig, CompressionConfig, ObserverCallback, StorageEvent, StorageCapabilities, StorageMetadata, TTLConfig, StrataConfig, StorageChange, SubscriptionCallback, UnsubscribeFunction, } from "./types/index.js";
22
22
  export { isValidKey, isValidValue, serializeValue, deserializeValue, generateId, createError, retry, debounce, throttle, } from "./utils/index.js";
23
23
  import { Strata } from "./core/Strata.js";
24
- import type { StrataConfig } from "./types.js";
24
+ import type { StrataConfig } from "./types/index.js";
25
25
  /**
26
26
  * Register the standard web storage adapters on a Strata instance: memory,
27
27
  * localStorage, sessionStorage, indexedDB, cookies, and cache. Exported so any
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strata-storage",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "Zero-dependency universal storage plugin providing a unified API for all storage operations across web, Android, and iOS platforms",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strata-storage",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "Zero-dependency universal storage plugin providing a unified API for all storage operations across web, Android, and iOS platforms",
5
5
  "type": "module",
6
6
  "packageManager": "yarn@4.17.0",
package/scripts/build.js CHANGED
@@ -83,20 +83,37 @@ const fixPathAliases = (dir) => {
83
83
  .replace(/from\s+["']\.\/utils["']/g, `from "./utils/index.js"`)
84
84
  .replace(/from\s+["']\.\/plugin["']/g, `from "./plugin/index.js"`);
85
85
 
86
- // Add .js extension to relative imports
86
+ // Add the correct extension to extensionless relative imports.
87
+ // Directory-aware: a barrel import like './types' must resolve to
88
+ // './types/index.js', a file import like './core/Strata' to
89
+ // './core/Strata.js'. tsc has already emitted every .js by now, so we
90
+ // resolve against the real dist files instead of blindly appending '.js'
91
+ // (the previous naive appender produced a dead './types.js' that broke
92
+ // type resolution for every TS consumer).
93
+ const fileDir = path.dirname(filePath);
94
+ const resolveRel = (importPath) => {
95
+ if (importPath.endsWith('.js') || importPath.endsWith('.json')) {
96
+ return importPath;
97
+ }
98
+ const abs = path.resolve(fileDir, importPath);
99
+ if (fs.existsSync(`${abs}.js`)) {
100
+ return `${importPath}.js`;
101
+ }
102
+ if (fs.existsSync(path.join(abs, 'index.js'))) {
103
+ return `${importPath.replace(/\/+$/, '')}/index.js`;
104
+ }
105
+ // Fall back to the legacy behavior if nothing matched on disk.
106
+ return `${importPath}.js`;
107
+ };
87
108
  content = content
88
- .replace(/from\s+["'](\.[^"']+)["']/g, (match, path) => {
89
- if (!path.endsWith('.js') && !path.endsWith('.json')) {
90
- return `from "${path}.js"`;
91
- }
92
- return match;
93
- })
94
- .replace(/import\(["'](\.[^"']+)["']\)/g, (match, path) => {
95
- if (!path.endsWith('.js') && !path.endsWith('.json')) {
96
- return `import("${path}.js")`;
97
- }
98
- return match;
99
- });
109
+ .replace(
110
+ /from\s+["'](\.[^"']+)["']/g,
111
+ (_match, p) => `from "${resolveRel(p)}"`,
112
+ )
113
+ .replace(
114
+ /import\(["'](\.[^"']+)["']\)/g,
115
+ (_match, p) => `import("${resolveRel(p)}")`,
116
+ );
100
117
 
101
118
  fs.writeFileSync(filePath, content);
102
119
  }