Skip to content

Commit 91e6a13

Browse files
committed
chore(release): bump and build v2.0.0
1 parent 327d45d commit 91e6a13

11 files changed

Lines changed: 791 additions & 389 deletions

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Changelog
2+
3+
## 2.0.0
4+
5+
- Add synchronous cache primitives: `find`, `has`, `patch`, `evict`, `inspect`, `reset`, and `resetAll`.
6+
- Ship TypeScript declarations for the public Store and ModelCache APIs.
7+
- Modernize package output to ESM (`dist/backbone.store.mjs`), CJS (`dist/backbone.store.cjs`), and UMD (`dist/backbone.store.js`) through an `exports` map.
8+
- Add `"type": "module"` for native ESM source and tooling.
9+
- Change raw CommonJS consumption to use the default export: `require('backbone.store').default`.
10+
- Remove the old `dist/backbone.store.esm.js` filename; use `dist/backbone.store.mjs` or the package root import instead.
11+
- Keep UMD assignment on `Backbone.Store` without adding a `window.Store` global.
12+
- Upgrade the test/build stack for Node 24 and replace Babel/Rollup/nyc with native ESM, tsdown, ESLint flat config, and c8.
13+
14+
### Fixes
15+
16+
- Prevent id-key collisions for ids such as `"toString"` and `"__proto__"` by using null-prototype cache storage.
17+
- Track pending no-id models so Store-owned listeners are cleaned up if the model is reset, removed, or destroyed before receiving an id.
18+
- Detach Store-owned `destroy` listeners when `Store.remove(modelName)`, `Store.removeAll()`, `Store.reset(modelName)`, or `Store.resetAll()` clears cached instances.
19+
- Avoid object-inspection conflicts when assertion/debug tools call `Store.inspect(depth, options)` while formatting Store references.

dist/backbone.store.cjs

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
Object.defineProperties(exports, {
2+
__esModule: { value: true },
3+
[Symbol.toStringTag]: { value: "Module" }
4+
});
5+
//#region \0rolldown/runtime.js
6+
var __create = Object.create;
7+
var __defProp = Object.defineProperty;
8+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9+
var __getOwnPropNames = Object.getOwnPropertyNames;
10+
var __getProtoOf = Object.getPrototypeOf;
11+
var __hasOwnProp = Object.prototype.hasOwnProperty;
12+
var __copyProps = (to, from, except, desc) => {
13+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14+
key = keys[i];
15+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16+
get: ((k) => from[k]).bind(null, key),
17+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18+
});
19+
}
20+
return to;
21+
};
22+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23+
value: mod,
24+
enumerable: true
25+
}) : target, mod));
26+
//#endregion
27+
let underscore = require("underscore");
28+
let backbone = require("backbone");
29+
backbone = __toESM(backbone, 1);
30+
//#region lib/model-cache.js
31+
function createCache() {
32+
return Object.create(null);
33+
}
34+
function ModelCache(Model, modelName) {
35+
this.instances = createCache();
36+
this.pending = [];
37+
this.Model = Model;
38+
this.modelName = modelName;
39+
this.ModelConstructor = this._getConstructor(Model);
40+
}
41+
(0, underscore.extend)(ModelCache.prototype, {
42+
_getConstructor(Model) {
43+
const cache = this;
44+
const ModelConstructor = function(attrs, options) {
45+
return cache.get(attrs, options);
46+
};
47+
(0, underscore.extend)(ModelConstructor, Model);
48+
ModelConstructor.prototype = this.Model.prototype;
49+
return ModelConstructor;
50+
},
51+
get(attrs, options) {
52+
const instanceKey = this._getKey(attrs);
53+
const instance = this.find(instanceKey);
54+
if (!instance) return this._new(attrs, options);
55+
instance.set(attrs);
56+
Store.trigger("update", instance, this);
57+
return instance;
58+
},
59+
find(id) {
60+
const key = this._key(id);
61+
if (key == null) return;
62+
return this.instances[key];
63+
},
64+
has(id) {
65+
return !!this.find(id);
66+
},
67+
patch(id, attrs, options) {
68+
const instance = this.find(id);
69+
if (!instance) return;
70+
instance.set(attrs, options);
71+
Store.trigger("update", instance, this);
72+
return instance;
73+
},
74+
evict(id) {
75+
return this._removeKey(this._key(id));
76+
},
77+
inspect(id) {
78+
const key = this._key(id);
79+
const model = key == null ? void 0 : this.instances[key];
80+
return {
81+
modelName: this.modelName,
82+
id,
83+
key,
84+
cached: !!model,
85+
model
86+
};
87+
},
88+
reset() {
89+
Object.keys(this.instances).forEach((key) => {
90+
this._detachCached(this.instances[key]);
91+
});
92+
this.pending.slice().forEach((instance) => {
93+
this._detachPending(instance);
94+
});
95+
this.instances = createCache();
96+
this.pending = [];
97+
},
98+
_new(attrs, options) {
99+
const instance = new this.Model(attrs, options);
100+
if (instance.isNew()) this._trackPending(instance);
101+
else this._add(instance);
102+
return instance;
103+
},
104+
_add(instance) {
105+
const key = this._getModelKey(instance);
106+
this._detachPending(instance);
107+
if (key == null) return;
108+
if (this.instances[key]) return;
109+
this.instances[key] = instance;
110+
this._attachCached(instance);
111+
Store.trigger("add", instance, this);
112+
},
113+
remove(instance) {
114+
this._removeKey(this._getModelKey(instance), instance);
115+
return instance;
116+
},
117+
_getKey(attrs) {
118+
return attrs && this._key(attrs[this.Model.prototype.idAttribute]);
119+
},
120+
_getModelKey(instance) {
121+
if (!instance) return;
122+
return this._key(instance.id);
123+
},
124+
_key(id) {
125+
if (id == null) return;
126+
return String(id);
127+
},
128+
_trackPending(instance) {
129+
this.pending.push(instance);
130+
instance.once(`change:${instance.idAttribute}`, this._add, this);
131+
instance.on("destroy", this._removePending, this);
132+
},
133+
_removePending(instance) {
134+
this._detachPending(instance);
135+
},
136+
_detachPending(instance) {
137+
const index = this.pending.indexOf(instance);
138+
if (index > -1) this.pending.splice(index, 1);
139+
instance.off(`change:${instance.idAttribute}`, this._add, this);
140+
instance.off("destroy", this._removePending, this);
141+
},
142+
_attachCached(instance) {
143+
instance.on("destroy", this.remove, this);
144+
},
145+
_detachCached(instance) {
146+
instance.off("destroy", this.remove, this);
147+
},
148+
_removeKey(key, eventInstance) {
149+
if (key == null) return;
150+
const instance = this.instances[key];
151+
if (!instance) return;
152+
delete this.instances[key];
153+
this._detachCached(instance);
154+
Store.trigger("remove", eventInstance || instance, this);
155+
return instance;
156+
}
157+
});
158+
//#endregion
159+
//#region lib/index.js
160+
let ModelCaches = {};
161+
const STORE_INSPECT_LABEL = "[Backbone.Store]";
162+
const nodeInspectSymbol = typeof Symbol === "function" && Symbol.for && Symbol.for("nodejs.util.inspect.custom");
163+
function isCustomInspectCall(modelName, id) {
164+
return typeof modelName === "number" && (id == null || typeof id === "object");
165+
}
166+
/**
167+
* Store wrapper converts regular Backbone models into unique ones.
168+
*
169+
* Example:
170+
* const StoredUser = Store(User);
171+
*/
172+
function Store(Model, modelName = (0, underscore.uniqueId)("Store_")) {
173+
return Store.add(Model, modelName).ModelConstructor;
174+
}
175+
(0, underscore.extend)(Store, backbone.default.Events, {
176+
ModelCache,
177+
add(Model, modelName) {
178+
if (!modelName) throw "Model name required";
179+
if (ModelCaches[modelName]) return ModelCaches[modelName];
180+
return ModelCaches[modelName] = new Store.ModelCache(Model, modelName);
181+
},
182+
getCache(modelName) {
183+
if (!ModelCaches[modelName]) throw `Unrecognized Model: "${modelName}"`;
184+
return ModelCaches[modelName];
185+
},
186+
getAllCache() {
187+
return (0, underscore.clone)(ModelCaches);
188+
},
189+
get(modelName) {
190+
return Store.getCache(modelName).ModelConstructor;
191+
},
192+
find(modelName, id) {
193+
return Store.getCache(modelName).find(id);
194+
},
195+
has(modelName, id) {
196+
return Store.getCache(modelName).has(id);
197+
},
198+
patch(modelName, id, attrs, options) {
199+
return Store.getCache(modelName).patch(id, attrs, options);
200+
},
201+
evict(modelName, id) {
202+
return Store.getCache(modelName).evict(id);
203+
},
204+
inspect(modelName, id) {
205+
if (isCustomInspectCall(modelName, id)) return STORE_INSPECT_LABEL;
206+
return Store.getCache(modelName).inspect(id);
207+
},
208+
getAll() {
209+
return (0, underscore.reduce)(ModelCaches, (all, cache, modelName) => {
210+
all[modelName] = cache.ModelConstructor;
211+
return all;
212+
}, {});
213+
},
214+
reset(modelName) {
215+
Store.getCache(modelName).reset();
216+
},
217+
resetAll() {
218+
(0, underscore.each)(ModelCaches, (cache) => {
219+
cache.reset();
220+
});
221+
},
222+
remove(modelName) {
223+
if (!ModelCaches[modelName]) return;
224+
ModelCaches[modelName].reset();
225+
delete ModelCaches[modelName];
226+
},
227+
removeAll() {
228+
Store.resetAll();
229+
ModelCaches = {};
230+
}
231+
});
232+
if (nodeInspectSymbol) Store[nodeInspectSymbol] = () => STORE_INSPECT_LABEL;
233+
backbone.default.Store = Store;
234+
//#endregion
235+
exports.ModelCache = ModelCache;
236+
exports.default = Store;
237+
238+
//# sourceMappingURL=backbone.store.cjs.map

dist/backbone.store.cjs.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)