notu 0.6.2 → 0.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.
package/dist/notu.mjs CHANGED
@@ -40,7 +40,6 @@ class Attr extends ModelWithState {
40
40
  __publicField(this, "_name", "");
41
41
  __publicField(this, "_description", "");
42
42
  __publicField(this, "_type", "TEXT");
43
- __publicField(this, "_spaceId", 0);
44
43
  __publicField(this, "_space", null);
45
44
  name && (this.name = name), description && (this.description = description);
46
45
  }
@@ -88,29 +87,26 @@ class Attr extends ModelWithState {
88
87
  asDate() {
89
88
  return this.type = "DATE", this;
90
89
  }
91
- get spaceId() {
92
- return this._spaceId;
93
- }
94
- set spaceId(value) {
95
- var _a;
96
- value !== this._spaceId && (this._spaceId = value, value !== ((_a = this.space) == null ? void 0 : _a.id) && (this._space = null), this.isClean && this.dirty());
97
- }
98
90
  get space() {
99
91
  return this._space;
100
92
  }
101
93
  set space(value) {
102
- this._space = value, this.spaceId = (value == null ? void 0 : value.id) ?? 0;
94
+ var _a;
95
+ if (value !== this._space) {
96
+ const idChanged = (value == null ? void 0 : value.id) != ((_a = this._space) == null ? void 0 : _a.id);
97
+ this._space = value, this.isClean && idChanged && this.dirty();
98
+ }
103
99
  }
104
100
  in(space) {
105
- return typeof space == "number" ? this.spaceId = space : this.space = space, this;
101
+ return this.space = space, this;
106
102
  }
107
103
  duplicate() {
108
104
  const output = new Attr();
109
- return output.id = this.id, output.name = this.name, output.description = this.description, output.type = this.type, this.space ? output.space = this.space : output.spaceId = this.spaceId, output.state = this.state, output;
105
+ return output.id = this.id, output.name = this.name, output.description = this.description, output.type = this.type, output.space = this.space, output.state = this.state, output;
110
106
  }
111
107
  validate(throwError = !1) {
112
108
  let output = null;
113
- if (this.spaceId <= 0 ? output = "Note spaceId must be greater than zero." : !this.isNew && this.id <= 0 && (output = "Attr id must be greater than zero if in non-new state."), throwError && output != null)
109
+ if (this.space ? !this.isNew && this.id <= 0 && (output = "Attr id must be greater than zero if in non-new state.") : output = "Attr must belong to a space.", throwError && output != null)
114
110
  throw Error(output);
115
111
  return output == null;
116
112
  }
@@ -127,158 +123,102 @@ class Attr extends ModelWithState {
127
123
  }
128
124
  }
129
125
  toJSON() {
126
+ var _a;
130
127
  return {
131
128
  state: this.state,
132
129
  id: this.id,
133
130
  name: this.name,
134
131
  description: this.description,
135
132
  type: this.type,
136
- spaceId: this.spaceId
133
+ spaceId: (_a = this.space) == null ? void 0 : _a.id
137
134
  };
138
135
  }
139
- static fromJSON(json) {
140
- const output = new Attr(json.name, json.description);
141
- return output.type = json.type, output.spaceId = json.spaceId, output.id = json.id, output.state = json.state, output;
142
- }
143
136
  }
144
- class CachedClient {
145
- constructor(internalClient) {
146
- __publicField(this, "_internalClient");
147
- //Caches
148
- __publicField(this, "_spaces", null);
149
- __publicField(this, "_attrs", null);
150
- __publicField(this, "_tags", null);
151
- this._internalClient = internalClient;
137
+ class Notu {
138
+ constructor(client, cache) {
139
+ __publicField(this, "_client");
140
+ __publicField(this, "_cache");
141
+ this._client = client, this._cache = cache;
152
142
  }
153
- _linkTagsToSpaces() {
154
- for (const tag of this._tags.values()) {
155
- const space = this._spaces.get(tag.spaceId);
156
- space && (tag.space = space);
157
- }
143
+ get client() {
144
+ return this._client;
158
145
  }
159
- _linkAttrsToSpaces() {
160
- for (const attr of this._attrs.values()) {
161
- const space = this._spaces.get(attr.spaceId);
162
- space && (attr.space = space);
163
- }
146
+ get cache() {
147
+ return this._cache;
164
148
  }
165
- /////////////////////////////////////
166
- // NotuClient implementation start //
167
- /////////////////////////////////////
168
149
  async login(username, password) {
169
- return await this._internalClient.login(username, password);
170
- }
171
- async getSpaces() {
172
- if (this._spaces == null) {
173
- const spaces = await this._internalClient.getSpaces();
174
- this._spaces = /* @__PURE__ */ new Map();
175
- for (const space of spaces)
176
- this._spaces.set(space.id, space);
177
- this._tags != null && this._linkTagsToSpaces(), this._attrs != null && this._linkAttrsToSpaces();
178
- }
179
- return [...this._spaces.values()];
150
+ return await this.client.login(username, password);
151
+ }
152
+ async setup() {
153
+ return await this.client.setup();
154
+ }
155
+ getSpaces() {
156
+ return this.cache.getSpaces();
157
+ }
158
+ getSpace(id) {
159
+ return this.cache.getSpace(id);
160
+ }
161
+ getSpaceByName(name) {
162
+ return this.cache.getSpaceByName(name);
180
163
  }
181
164
  async saveSpace(space) {
182
- const saveResult = await this._internalClient.saveSpace(space);
183
- return this._spaces != null && this._spaces.set(saveResult.id, saveResult), saveResult;
184
- }
185
- async getAttrs(spaceId) {
186
- if (this._attrs == null) {
187
- const attrs = await this._internalClient.getAttrs(spaceId);
188
- this._attrs = /* @__PURE__ */ new Map();
189
- for (const attr of attrs)
190
- this._attrs.set(attr.id, attr);
191
- this._spaces != null && this._linkAttrsToSpaces();
192
- }
193
- return [...this._attrs.values()];
165
+ const spaceData = await this.client.saveSpace(space);
166
+ return this.cache.spaceSaved(spaceData);
167
+ }
168
+ getAttrs(space = null) {
169
+ return this.cache.getAttrs(space);
170
+ }
171
+ getAttr(id) {
172
+ return this.cache.getAttr(id);
173
+ }
174
+ getAttrByName(name, space) {
175
+ return this.cache.getAttrByName(name, space);
194
176
  }
195
177
  async saveAttr(attr) {
196
- const saveResult = await this._internalClient.saveAttr(attr);
197
- return this._attrs != null && this._attrs.set(saveResult.id, saveResult), saveResult;
198
- }
199
- async getTags() {
200
- if (this._tags == null) {
201
- const tags = await this._internalClient.getTags();
202
- this._tags = /* @__PURE__ */ new Map();
203
- for (const tag of tags)
204
- this._tags.set(tag.id, tag);
205
- this._spaces != null && this._linkTagsToSpaces();
206
- }
207
- return [...this._tags.values()];
178
+ const attrData = await this.client.saveAttr(attr);
179
+ return this.cache.attrSaved(attrData);
180
+ }
181
+ getTags(space = null, includeOtherSpacePublics = !1) {
182
+ return this.cache.getTags(space, includeOtherSpacePublics);
183
+ }
184
+ getTag(id) {
185
+ return this.cache.getTag(id);
186
+ }
187
+ getTagByName(name, space) {
188
+ return this.cache.getTagByName(name, space);
208
189
  }
209
190
  async getNotes(query, spaceId) {
210
- const results = await this._internalClient.getNotes(query, spaceId);
211
- if (this._spaces != null)
212
- for (const note of results) {
213
- const space = this._spaces.get(note.spaceId);
214
- space && (note.space = space);
215
- }
216
- if (this._attrs != null)
217
- for (const note of results)
218
- for (const na of note.allAttrs) {
219
- const attr = this._attrs.get(na.attrId);
220
- attr && (na.attr = attr, attr.isDate && !(na.value instanceof Date) && (na.value = new Date(na.value)), na.clean());
221
- }
222
- if (this._tags != null)
223
- for (const note of results) {
224
- {
225
- const tag = this._tags.get(note.id);
226
- tag && (note.setOwnTag(tag), note.clean(), note.ownTag.clean());
227
- }
228
- for (const nt of note.tags) {
229
- const tag = this._tags.get(nt.tagId);
230
- tag && (nt.tag = tag, nt.clean());
231
- }
232
- for (const na of note.allAttrs.filter((x) => x.tagId != null)) {
233
- const tag = this._tags.get(na.tagId);
234
- tag && (na.tag = tag, na.clean());
235
- }
236
- }
237
- return results;
191
+ return (await this.client.getNotes(query, spaceId)).map((n) => this.cache.noteFromJSON(n));
238
192
  }
239
193
  async getNoteCount(query, spaceId) {
240
- return await this._internalClient.getNoteCount(query, spaceId);
194
+ return await this.client.getNoteCount(query, spaceId);
241
195
  }
242
196
  async saveNotes(notes) {
243
- const saveResults = await this._internalClient.saveNotes(notes);
244
- if (this._tags != null)
245
- for (const note of saveResults.filter((x) => !!x.ownTag))
246
- this._tags.set(note.ownTag.id, note.ownTag);
247
- return saveResults;
197
+ const notesData = await this.client.saveNotes(notes);
198
+ for (const noteData of notesData.filter((x) => !!x.ownTag))
199
+ this.cache.tagSaved(noteData.ownTag);
200
+ return notes = notesData.map((n) => this.cache.noteFromJSON(n)), notes;
248
201
  }
249
202
  async customJob(name, data) {
250
- return await this._internalClient.customJob(name, data);
251
- }
252
- ///////////////////////////////////
253
- // NotuClient implementation end //
254
- ///////////////////////////////////
255
- //Synchronous cache methods
256
- //These are introduced to make it far easier to lookup the cached values
257
- //Especially in React where async is kind of a pain
258
- //Make sure this method has been called before any of the other methods in this section
259
- async cacheAll(spaceId = 0) {
260
- await this.getSpaces();
261
- const tagsPromise = this.getTags(), attrsPromise = this.getAttrs(spaceId);
262
- await Promise.all([tagsPromise, attrsPromise]);
263
- }
264
- get spaces() {
265
- return [...this._spaces.values()];
266
- }
267
- get tags() {
268
- return [...this._tags.values()];
269
- }
270
- get attrs() {
271
- return [...this._attrs.values()];
203
+ return await this.client.customJob(name, data);
272
204
  }
273
205
  }
274
206
  class Space extends ModelWithState {
275
207
  constructor(name = "") {
276
208
  super();
277
- __publicField(this, "id", 0);
209
+ __publicField(this, "_id", 0);
278
210
  __publicField(this, "_name", "");
279
211
  __publicField(this, "_version", "0.0.1");
280
212
  this._name = name;
281
213
  }
214
+ get id() {
215
+ return this._id;
216
+ }
217
+ set id(value) {
218
+ if (!this.isNew)
219
+ throw Error("Cannot change the id of a Space once it has already been created.");
220
+ this._id = value;
221
+ }
282
222
  get name() {
283
223
  return this._name;
284
224
  }
@@ -296,7 +236,7 @@ class Space extends ModelWithState {
296
236
  }
297
237
  duplicate() {
298
238
  const output = new Space();
299
- return output.id = this.id, output.name = this.name, output.state = this.state, output.version = this.version, output;
239
+ return output.id = this.id, output.name = this.name, output.version = this.version, output.state = this.state, output;
300
240
  }
301
241
  validate(throwError = !1) {
302
242
  let output = null;
@@ -312,12 +252,8 @@ class Space extends ModelWithState {
312
252
  version: this.version
313
253
  };
314
254
  }
315
- static fromJSON(json) {
316
- const output = new Space(json.name);
317
- return output.id = json.id, output.state = json.state, output.version = json.version, output;
318
- }
319
255
  }
320
- class HttpClient {
256
+ class NotuHttpClient {
321
257
  constructor(url, fetchMethod = null) {
322
258
  __publicField(this, "_url", null);
323
259
  __publicField(this, "_token", null);
@@ -337,81 +273,60 @@ class HttpClient {
337
273
  this._token = value;
338
274
  }
339
275
  async login(username, password) {
340
- const result = await this._fetch(
276
+ const response = await this._fetch(
341
277
  this.url + "/login",
342
278
  {
343
279
  method: "POST",
344
280
  body: JSON.stringify({ username, password })
345
281
  }
346
282
  );
347
- if (result.body != null) {
348
- const token = (await result.json()).token;
349
- if (token)
350
- return this._token = token, { success: !0, error: null, token: this._token };
283
+ if (response.body != null) {
284
+ const result = await response.json();
285
+ return result.token && (this._token = result.token), result;
351
286
  }
352
- return { success: !1, error: "Invalid username & password.", token: null };
287
+ return { token: null, error: "Unknown error occurred on the server" };
353
288
  }
354
- async getSpaces() {
355
- return (await (await this._fetch(
356
- this.url + "/spaces",
289
+ async setup() {
290
+ await (await this._fetch(
291
+ this.url + "/setup",
357
292
  {
358
- method: "GET",
293
+ method: "POST",
359
294
  headers: { Authorization: "Bearer " + this.token }
360
295
  }
361
- )).json()).map((x) => Space.fromJSON(x));
296
+ )).json();
362
297
  }
363
298
  async saveSpace(space) {
364
- const result = await this._fetch(
299
+ return await (await this._fetch(
365
300
  this.url + "/spaces",
366
301
  {
367
302
  method: "POST",
368
303
  body: JSON.stringify(space),
369
304
  headers: { Authorization: "Bearer " + this.token }
370
305
  }
371
- );
372
- return Space.fromJSON(await result.json());
373
- }
374
- async getAttrs(spaceId = 0) {
375
- return (await (await this._fetch(
376
- this.url + `/attrs?space=${spaceId}`,
377
- {
378
- method: "GET",
379
- headers: { Authorization: "Bearer " + this.token }
380
- }
381
- )).json()).map((x) => Attr.fromJSON(x));
306
+ )).json();
382
307
  }
383
308
  async saveAttr(attr) {
384
- const result = await this._fetch(
309
+ return await (await this._fetch(
385
310
  this.url + "/attrs",
386
311
  {
387
312
  method: "POST",
388
313
  body: JSON.stringify(attr),
389
314
  headers: { Authorization: "Bearer " + this.token }
390
315
  }
391
- );
392
- return Attr.fromJSON(await result.json());
393
- }
394
- async getTags() {
395
- return (await (await this._fetch(
396
- this.url + "/tags",
397
- {
398
- method: "GET",
399
- headers: { Authorization: "Bearer " + this.token }
400
- }
401
- )).json()).map((x) => Tag.fromJSON(x));
316
+ )).json();
402
317
  }
403
- async getNotes(query, spaceId) {
404
- return (await (await this._fetch(
405
- this.url + `/notes?space=${spaceId}&query=${encodeURIComponent(query)}`,
318
+ async getNotes(query, space) {
319
+ return space instanceof Space && (space = space.id), await (await this._fetch(
320
+ this.url + `/notes?space=${space}&query=${encodeURIComponent(query)}`,
406
321
  {
407
322
  method: "GET",
408
323
  headers: { Authorization: "Bearer " + this.token }
409
324
  }
410
- )).json()).map((x) => Note.fromJSON(x));
325
+ )).json();
411
326
  }
412
- async getNoteCount(query, spaceId) {
413
- return (await (await this._fetch(
414
- this.url + `/notes?count=true&space=${spaceId}&query=${encodeURIComponent(query)}`,
327
+ async getNoteCount(query, space) {
328
+ return space instanceof Space && (space = space.id), (await (await this._fetch(
329
+ this.url + `/notes?count=true&space=${space}&query=${encodeURIComponent(query)}`,
415
330
  {
416
331
  method: "GET",
417
332
  headers: { Authorization: "Bearer " + this.token }
@@ -419,14 +334,14 @@ class HttpClient {
419
334
  )).json()).count;
420
335
  }
421
336
  async saveNotes(notes) {
422
- return (await (await this._fetch(
337
+ return await (await this._fetch(
423
338
  this.url + "/notes",
424
339
  {
425
340
  method: "POST",
426
341
  body: JSON.stringify(notes),
427
342
  headers: { Authorization: "Bearer " + this.token }
428
343
  }
429
- )).json()).map((x) => Note.fromJSON(x));
344
+ )).json();
430
345
  }
431
346
  async customJob(name, data) {
432
347
  return await (await this._fetch(
@@ -440,164 +355,130 @@ class HttpClient {
440
355
  }
441
356
  }
442
357
  class NoteAttr extends ModelWithState {
443
- constructor(note, attr, value) {
358
+ constructor(attr, tag, value) {
444
359
  super();
445
- __publicField(this, "_noteId", 0);
446
- __publicField(this, "_note", null);
447
- __publicField(this, "_attrId", 0);
360
+ __publicField(this, "_tag", null);
448
361
  __publicField(this, "_attr", null);
449
362
  __publicField(this, "_value", null);
450
- __publicField(this, "_tagId", null);
451
- __publicField(this, "_tag", null);
452
- note != null && note != null && (typeof note == "number" ? this.noteId = note : this.note = note), attr != null && attr != null && (typeof attr == "number" ? this.attrId = attr : this.attr = attr), value != null && value != null && (this.value = value);
453
- }
454
- get noteId() {
455
- return this._noteId;
456
- }
457
- set noteId(value) {
458
- var _a;
459
- value !== this._noteId && (this._noteId = value, value !== ((_a = this.note) == null ? void 0 : _a.id) && (this._note = null), this.isClean && this.dirty());
460
- }
461
- get note() {
462
- return this._note;
463
- }
464
- set note(value) {
465
- this._note = value, this.noteId = (value == null ? void 0 : value.id) ?? 0;
466
- }
467
- get attrId() {
468
- return this._attrId;
363
+ if (!attr)
364
+ throw Error("Cannot instanciate new NoteAttr without a passed in attr.");
365
+ if (attr.isNew)
366
+ throw Error("Cannot create a NoteAttr object for an attr that hasn't been saved yet.");
367
+ if (attr.isDeleted)
368
+ throw Error("Cannot create a NoteAttr object for an attr marked as deleted.");
369
+ if (this._attr = attr, tag) {
370
+ if (tag.isNew)
371
+ throw Error("Cannot create a NoteAttr object linked to a tag that hasn't been saved yet.");
372
+ if (tag.isDeleted)
373
+ throw Error("Cannot create a NoteAttr object linked to a tag marked as deleted.");
374
+ this._tag = tag;
375
+ }
376
+ value != null && value != null ? this.value = value : this.value = attr.defaultValue;
469
377
  }
470
- set attrId(value) {
471
- var _a;
472
- value !== this._attrId && (this._attrId = value, value !== ((_a = this.attr) == null ? void 0 : _a.id) && (this._attr = null), this.isClean && this.dirty());
378
+ get tag() {
379
+ return this._tag;
473
380
  }
474
381
  get attr() {
475
382
  return this._attr;
476
383
  }
477
- set attr(newAttr) {
478
- const oldAttr = this._attr;
479
- this._attr = newAttr, newAttr ? newAttr.id != this.attrId && (!oldAttr || newAttr.type != oldAttr.type) && (this.value = newAttr.defaultValue) : this.value = null, this.attrId = (newAttr == null ? void 0 : newAttr.id) ?? 0;
480
- }
481
384
  get value() {
482
385
  return this._value;
483
386
  }
484
387
  set value(newVal) {
485
- newVal != this._value && (this._value = newVal, this.isClean && this.dirty());
388
+ this.attr.isDate && !(newVal instanceof Date) && (newVal = new Date(newVal)), newVal != this._value && (this._value = newVal, this.isClean && this.dirty());
486
389
  }
487
390
  withValue(value) {
488
391
  return this.value = value, this;
489
392
  }
490
- get tagId() {
491
- return this._tagId;
492
- }
493
- set tagId(value) {
494
- var _a;
495
- value !== this._tagId && (this._tagId = value, value !== ((_a = this.tag) == null ? void 0 : _a.id) && (this._tag = null), this.isClean && this.dirty());
496
- }
497
- get tag() {
498
- return this._tag;
499
- }
500
- set tag(value) {
501
- this._tag = value, this.tagId = (value == null ? void 0 : value.id) ?? null;
502
- }
503
- onTag(tag) {
504
- return typeof tag == "number" ? this.tagId = tag : this.tag = tag, this;
505
- }
506
393
  duplicate() {
507
- const output = new NoteAttr();
508
- return output.noteId = this.noteId, this.attr ? output.attr = this.attr : output.attrId = this.attrId, this.tag ? output.tag = this.tag : output.tagId = this.tagId, output.value = this.value, output.state = this.state, output;
394
+ return new NoteAttr(this.attr, this.tag, this.value);
509
395
  }
510
396
  validate(throwError = !1) {
511
397
  let output = null;
512
- if (this.noteId <= 0 && !this.isNew ? output = "NoteAttr noteId must be greater than zero" : this.attrId <= 0 && (output = "NoteAttr attrId must be greater than zero"), throwError && output != null)
398
+ if (throwError && output != null)
513
399
  throw Error(output);
514
400
  return output == null;
515
401
  }
516
402
  toJSON() {
403
+ var _a;
517
404
  return {
518
405
  state: this.state,
519
- noteId: this.noteId,
520
- attrId: this.attrId,
521
- tagId: this.tagId,
406
+ attrId: this.attr.id,
407
+ tagId: (_a = this.tag) == null ? void 0 : _a.id,
522
408
  value: this.value
523
409
  };
524
410
  }
525
- static fromJSON(json) {
526
- const output = new NoteAttr(json.noteId, json.attrId, json.value);
527
- return output.tagId = json.tagId, output.state = json.state, output;
528
- }
529
411
  }
530
412
  class NoteTag extends ModelWithState {
531
- constructor(note, tag) {
413
+ constructor(tag) {
532
414
  super();
533
- __publicField(this, "_noteId", 0);
534
- __publicField(this, "_note", null);
535
- __publicField(this, "_tagId", 0);
536
- __publicField(this, "_tag", null);
537
- note != null && note != null && (typeof note == "number" ? this.noteId = note : this.note = note), tag != null && tag != null && (typeof tag == "number" ? this.tagId = tag : this.tag = tag);
538
- }
539
- get noteId() {
540
- return this._noteId;
541
- }
542
- set noteId(value) {
543
- var _a;
544
- value !== this._noteId && (this._noteId = value, value !== ((_a = this.note) == null ? void 0 : _a.id) && (this._note = null), this.isClean && this.dirty());
545
- }
546
- get note() {
547
- return this._note;
415
+ __publicField(this, "_tag");
416
+ __publicField(this, "_attrs", []);
417
+ if (!tag)
418
+ throw Error("Cannot instanciate new NoteTag without a passed in tag.");
419
+ if (tag.isNew)
420
+ throw Error("Cannot create a NoteTag object for a tag that hasn't been saved yet.");
421
+ if (tag.isDeleted)
422
+ throw Error("Cannot create a NoteTag object for a tag marked as deleted.");
423
+ this._tag = tag;
548
424
  }
549
- set note(value) {
550
- this._note = value, this.noteId = (value == null ? void 0 : value.id) ?? 0;
425
+ get tag() {
426
+ return this._tag;
551
427
  }
552
- get tagId() {
553
- return this._tagId;
428
+ get attrs() {
429
+ return this._attrs.filter((x) => !x.isDeleted);
554
430
  }
555
- set tagId(value) {
556
- var _a;
557
- value !== this._tagId && (this._tagId = value, value !== ((_a = this.tag) == null ? void 0 : _a.id) && (this._tag = null), this.isClean && this.dirty());
431
+ get attrsPendingDeletion() {
432
+ return this._attrs.filter((x) => x.isDeleted);
558
433
  }
559
- get tag() {
560
- return this._tag;
434
+ addAttr(attr, value) {
435
+ if (attr.isDeleted)
436
+ throw Error("Cannot add an attribute marked as deleted.");
437
+ if (attr.isNew)
438
+ throw Error("Cannot add an attribute that hasn't yet been saved.");
439
+ const na = new NoteAttr(attr, this.tag, value);
440
+ return this._attrs.push(na), this;
561
441
  }
562
- set tag(value) {
563
- this._tag = value, this.tagId = (value == null ? void 0 : value.id) ?? 0;
442
+ removeAttr(attr) {
443
+ const na = this._attrs.find((x) => x.attr.id == attr.id);
444
+ return na ? (na.isNew ? this._attrs = this._attrs.filter((x) => x !== na) : na.delete(), this) : this;
564
445
  }
565
- get attrs() {
566
- return this.note ? this.note.allAttrs.filter((x) => x.tagId == this.tagId) : [];
446
+ getAttr(attr) {
447
+ return attr instanceof Attr && (attr = attr.name), this.attrs.find((x) => x.attr.name == attr);
567
448
  }
568
- addAttr(attr) {
569
- if (!this.note)
570
- throw new Error("Cannot call addAttr on NoteTag where note property has not been set");
571
- const na = this.note.addAttr(attr);
572
- return na.tag = this.tag, na;
449
+ getValue(attr) {
450
+ var _a;
451
+ return (_a = this.getAttr(attr)) == null ? void 0 : _a.value;
573
452
  }
574
453
  duplicate() {
575
- const output = new NoteTag();
576
- return output.noteId = this.noteId, this.tag ? output.tag = this.tag : output.tagId = this.tagId, output;
454
+ const output = new NoteTag(this.tag);
455
+ return output._attrs = this.attrs.map((x) => x.duplicate()), output;
577
456
  }
578
457
  validate(throwError = !1) {
579
- let output = null;
580
- if (this.noteId <= 0 && !this.isNew ? output = "NoteTag noteId must be greater than zero" : this.tagId <= 0 ? output = "NoteTag tagId must be greater than zero" : this.noteId == this.tagId && (output = "NoteTag cannot link a note to its own tag"), throwError && output != null)
581
- throw Error(output);
582
- return output == null;
458
+ function exit(message) {
459
+ if (throwError && message != null)
460
+ throw Error(message);
461
+ return message == null;
462
+ }
463
+ if (!this.tag)
464
+ return exit("NoteTag must have a tag set.");
465
+ for (const na of this._attrs)
466
+ if (!na.validate(throwError))
467
+ return !1;
468
+ return !0;
583
469
  }
584
470
  toJSON() {
585
471
  return {
586
472
  state: this.state,
587
- noteId: this.noteId,
588
- tagId: this.tagId
473
+ tagId: this.tag.id,
474
+ attrs: this._attrs.map((x) => x.toJSON())
589
475
  };
590
476
  }
591
- static fromJSON(json) {
592
- const output = new NoteTag(json.noteId, json.tagId);
593
- return output.state = json.state, output;
594
- }
595
477
  }
596
478
  class Tag extends ModelWithState {
597
479
  constructor(name = "") {
598
480
  super();
599
481
  __publicField(this, "_id", 0);
600
- __publicField(this, "_spaceId", 0);
601
482
  __publicField(this, "_space", null);
602
483
  __publicField(this, "_name", "");
603
484
  __publicField(this, "_color", null);
@@ -608,23 +489,22 @@ class Tag extends ModelWithState {
608
489
  return this._id;
609
490
  }
610
491
  set id(value) {
611
- value !== this._id && (this._id = value, this.isClean && this.dirty());
612
- }
613
- get spaceId() {
614
- return this._spaceId;
615
- }
616
- set spaceId(value) {
617
- var _a;
618
- value !== this._spaceId && (this._spaceId = value, value !== ((_a = this.space) == null ? void 0 : _a.id) && (this._space = null), this.isClean && this.dirty());
492
+ if (!this.isNew)
493
+ throw Error("Cannot change the id of a Tag once it has already been created.");
494
+ this._id = value;
619
495
  }
620
496
  get space() {
621
497
  return this._space;
622
498
  }
623
499
  set space(value) {
624
- this._space = value, this.spaceId = (value == null ? void 0 : value.id) ?? 0;
500
+ var _a;
501
+ if (value !== this._space) {
502
+ const idChanged = (value == null ? void 0 : value.id) != ((_a = this._space) == null ? void 0 : _a.id);
503
+ this._space = value, this.isClean && idChanged && this.dirty();
504
+ }
625
505
  }
626
506
  in(space) {
627
- return typeof space == "number" ? this.spaceId = space : this.space = space, this;
507
+ return this.space = space, this;
628
508
  }
629
509
  get name() {
630
510
  return this._name;
@@ -633,7 +513,8 @@ class Tag extends ModelWithState {
633
513
  value !== this._name && (this._name = value, this.isClean && this.dirty());
634
514
  }
635
515
  getQualifiedName(contextSpaceId) {
636
- return contextSpaceId == this.spaceId ? this.name : `${this.space.name}.${this.name}`;
516
+ var _a;
517
+ return contextSpaceId == ((_a = this.space) == null ? void 0 : _a.id) ? this.name : `${this.space.name}.${this.name}`;
637
518
  }
638
519
  get color() {
639
520
  return this._color;
@@ -668,38 +549,36 @@ class Tag extends ModelWithState {
668
549
  return hex ? (hex.startsWith("#") && (hex = hex.substring(1)), parseInt(hex, 16)) : null;
669
550
  }
670
551
  toJSON() {
552
+ var _a;
671
553
  return {
672
554
  state: this.state,
673
555
  id: this.id,
674
556
  name: this.name,
675
- spaceId: this.spaceId,
557
+ spaceId: (_a = this.space) == null ? void 0 : _a.id,
676
558
  color: this.color,
677
559
  isPublic: this.isPublic
678
560
  };
679
561
  }
680
- static fromJSON(json) {
681
- const output = new Tag(json.name);
682
- return output.id = json.id, output.spaceId = json.spaceId, output.color = json.color, output.isPublic = json.isPublic, output.state = json.state, output;
683
- }
684
562
  }
685
563
  class Note extends ModelWithState {
686
- constructor(text) {
564
+ constructor(text, ownTag) {
687
565
  super();
688
566
  __publicField(this, "_id", 0);
689
567
  __publicField(this, "_date", /* @__PURE__ */ new Date());
690
568
  __publicField(this, "_text", "");
691
- __publicField(this, "_spaceId", 0);
692
569
  __publicField(this, "_space", null);
693
570
  __publicField(this, "_ownTag", null);
694
571
  __publicField(this, "_tags", []);
695
572
  __publicField(this, "_attrs", []);
696
- text && (this.text = text);
573
+ text && (this.text = text), this._ownTag = ownTag;
697
574
  }
698
575
  get id() {
699
576
  return this._id;
700
577
  }
701
578
  set id(value) {
702
- this._id = value, this.ownTag && (this.ownTag.id = value);
579
+ if (!this.isNew)
580
+ throw Error("Cannot change the id of a Note once it has already been created.");
581
+ this._id = value, this.ownTag && this.ownTag.id != value && (this.ownTag.id = value);
703
582
  }
704
583
  get date() {
705
584
  return this._date;
@@ -716,42 +595,30 @@ class Note extends ModelWithState {
716
595
  set text(value) {
717
596
  value !== this._text && (this._text = value, this.isClean && this.dirty());
718
597
  }
719
- get spaceId() {
720
- return this._spaceId;
721
- }
722
- set spaceId(value) {
723
- var _a;
724
- value !== this._spaceId && (this._spaceId = value, value !== ((_a = this.space) == null ? void 0 : _a.id) && (this._space = null), this.isClean && this.dirty(), this._setOwnTagSpace());
725
- }
726
598
  get space() {
727
599
  return this._space;
728
600
  }
729
601
  set space(value) {
730
- this._space = value, this.spaceId = (value == null ? void 0 : value.id) ?? 0;
602
+ var _a;
603
+ if (value !== this._space) {
604
+ const idChanged = (value == null ? void 0 : value.id) != ((_a = this._space) == null ? void 0 : _a.id);
605
+ this._space = value, this.isClean && idChanged && this.dirty(), this._setOwnTagSpace();
606
+ }
731
607
  }
732
608
  in(space) {
733
- return typeof space == "number" ? this.spaceId = space : this.space = space, this;
609
+ return this.space = space, this;
734
610
  }
735
611
  get ownTag() {
736
612
  return this._ownTag;
737
613
  }
738
- setOwnTag(tag) {
739
- if (typeof tag == "string")
740
- this.ownTag == null && (this._ownTag = new Tag()), this.ownTag.name = tag, this.ownTag.id = this.id, this._setOwnTagSpace();
741
- else {
742
- if (this.ownTag)
743
- throw new Error("Note has already had its tag set. If you would like to change the tag name, call setTag with just a string specifying the new tag name.");
744
- if (tag.id != 0 && tag.id != this.id)
745
- throw new Error("Attempted to set tag to note with non-matching ID. Added tag id must either match the note id, which indicates that the tag has already been added to the note. Otherwise the tag id must be zero, indicating that the tag still needs to be added.");
746
- this._ownTag = tag;
747
- }
748
- return this;
614
+ setOwnTag(tagName) {
615
+ return this.ownTag == null ? (this._ownTag = new Tag(tagName), this.ownTag.id = this.id) : this.ownTag.isDeleted && this.ownTag.dirty(), this.ownTag.name = tagName, this._setOwnTagSpace(), this;
749
616
  }
750
617
  removeOwnTag() {
751
618
  return this.ownTag ? (this.ownTag.isNew ? this._ownTag = null : this.ownTag.delete(), this) : this;
752
619
  }
753
620
  _setOwnTagSpace() {
754
- this.ownTag && (this.space ? this.ownTag.space = this.space : this.ownTag.spaceId = this.spaceId);
621
+ this.ownTag && this.space && (this.ownTag.space = this.space);
755
622
  }
756
623
  get tags() {
757
624
  return this._tags.filter((x) => !x.isDeleted);
@@ -766,103 +633,83 @@ class Note extends ModelWithState {
766
633
  throw Error("Cannot add a tag that hasn't yet been saved to a note");
767
634
  if (tag.id == this.id)
768
635
  throw Error("Note cannot add its own tag as a linked tag");
769
- if (!tag.isPublic && tag.spaceId != this.spaceId)
636
+ if (!tag.isPublic && tag.space.id != this.space.id)
770
637
  throw Error("Cannot add a private tag from another space");
771
- let nt = this._tags.find((x) => x.tagId == tag.id);
772
- return nt ? (nt.isDeleted && nt.dirty(), nt) : (nt = new NoteTag(), nt.note = this, nt.tag = tag, this._tags.push(nt), nt);
638
+ let nt = this._tags.find((x) => x.tag.id == tag.id);
639
+ return nt ? (nt.isDeleted && nt.dirty(), nt) : (nt = new NoteTag(tag), this._tags.push(nt), nt);
773
640
  }
774
641
  removeTag(tag) {
775
- const nt = this._tags.find((x) => x.tagId == tag.id);
776
- if (!nt)
777
- return this;
778
- nt.isNew ? this._tags = this._tags.filter((x) => x !== nt) : nt.delete();
779
- for (const na of this._attrs.filter((x) => !x.isDeleted && x.tagId == tag.id))
780
- this.removeAttr(na.attr, na.tag);
781
- return this;
642
+ const nt = this._tags.find((x) => x.tag.id == tag.id);
643
+ return nt ? (nt.isNew ? this._tags = this._tags.filter((x) => x !== nt) : nt.delete(), this) : this;
782
644
  }
783
645
  getTag(tag, space = null) {
784
- return tag instanceof Tag && (tag = tag.name), space && space instanceof Space && (space = space.id), space != null ? this.tags.find((x) => x.tag.name == tag && x.tag.spaceId == space) : this.tags.find((x) => x.tag.name == tag && x.tag.spaceId == this.spaceId);
646
+ return tag instanceof Tag && (tag = tag.name), space && space instanceof Space && (space = space.id), space != null ? this.tags.find((x) => x.tag.name == tag && x.tag.space.id == space) : this.tags.find((x) => x.tag.name == tag && x.tag.space.id == this.space.id);
785
647
  }
786
- /** This contains all attrs that have been added either directly to the note, or to a tag on the note and are not due to be deleted. */
787
- get allAttrs() {
648
+ get attrs() {
788
649
  return this._attrs.filter((x) => !x.isDeleted);
789
650
  }
790
- /** This contains all attrs that have been added directly to the note and are not due to be deleted. */
791
- get noteAttrs() {
792
- return this._attrs.filter((x) => !x.isDeleted && !x.tag);
793
- }
794
- /** This contains all attrs that have been added either directly to the note, or to a tag on the note and are due to be deleted. */
795
- get allAttrsPendingDeletion() {
651
+ get attrsPendingDeletion() {
796
652
  return this._attrs.filter((x) => x.isDeleted);
797
653
  }
798
- addAttr(attr) {
654
+ addAttr(attr, value) {
799
655
  if (attr.isDeleted)
800
656
  throw Error("Cannot add an attribute marked as deleted to a note");
801
657
  if (attr.isNew)
802
658
  throw Error("Cannot add an attribute that hasn't yet been saved to a note");
803
- const na = new NoteAttr(this, attr);
804
- return this._attrs.push(na), na;
659
+ const na = new NoteAttr(attr, null, value);
660
+ return this._attrs.push(na), this;
805
661
  }
806
- removeAttr(attr, tag = null) {
807
- const na = this._attrs.find((x) => x.attrId == attr.id && x.tagId == (tag == null ? void 0 : tag.id));
662
+ removeAttr(attr) {
663
+ const na = this._attrs.find((x) => x.attr.id == attr.id);
808
664
  return na ? (na.isNew ? this._attrs = this._attrs.filter((x) => x !== na) : na.delete(), this) : this;
809
665
  }
666
+ getAttr(attr) {
667
+ return attr instanceof Attr && (attr = attr.name), this.attrs.find((x) => x.attr.name == attr);
668
+ }
810
669
  getValue(attr) {
811
670
  var _a;
812
- return attr instanceof Attr && (attr = attr.name), (_a = this.noteAttrs.find((x) => x.attr.name == attr)) == null ? void 0 : _a.value;
813
- }
814
- getAttr(attr) {
815
- return attr instanceof Attr && (attr = attr.name), this.noteAttrs.find((x) => x.attr.name == attr);
671
+ return (_a = this.getAttr(attr)) == null ? void 0 : _a.value;
816
672
  }
817
673
  duplicate() {
818
- const output = new Note();
819
- return output.id = this.id, output.date = this.date, output.text = this.text, this.space ? output.space = this.space : output.spaceId = this.spaceId, output._tags = this.tags.map((x) => {
820
- const ntCopy = x.duplicate();
821
- return ntCopy.note = output, ntCopy;
822
- }), output._attrs = this.allAttrs.map((x) => {
823
- const naCopy = x.duplicate();
824
- return naCopy.note = output, naCopy;
825
- }), this.ownTag && !this.ownTag.isDeleted && output.setOwnTag(this.ownTag.duplicate()), output.state = this.state, output;
674
+ var _a;
675
+ const output = new Note(this.text, (_a = this.ownTag) == null ? void 0 : _a.duplicate()).at(this.date).in(this.space);
676
+ return output._attrs = this.attrs.map((x) => x.duplicate()), output._tags = this.tags.map((x) => x.duplicate()), output;
826
677
  }
827
678
  toJSON() {
679
+ var _a;
828
680
  return {
829
681
  state: this.state,
830
682
  id: this.id,
831
683
  date: this.date,
832
684
  text: this.text,
833
- spaceId: this.spaceId,
834
- ownTag: this.ownTag,
835
- tags: this.tags,
836
- attrs: this._attrs
685
+ spaceId: this.space.id,
686
+ ownTag: (_a = this.ownTag) == null ? void 0 : _a.toJSON(),
687
+ tags: this._tags.map((x) => x.toJSON()),
688
+ attrs: this._attrs.map((x) => x.toJSON())
837
689
  };
838
690
  }
839
- static fromJSON(json) {
840
- const output = new Note(json.text);
841
- if (output.id = json.id, output.date = new Date(json.date), output.spaceId = json.spaceId, json.ownTag && output.setOwnTag(Tag.fromJSON(json.ownTag)), json.tags) {
842
- output._tags = json.tags.map((x) => NoteTag.fromJSON(x));
843
- for (const nt of output._tags)
844
- nt.note = output;
845
- }
846
- if (json.attrs) {
847
- output._attrs = json.attrs.map((x) => NoteAttr.fromJSON(x));
848
- for (const na of output._attrs)
849
- na.note = output;
850
- }
851
- return output.state = json.state, output;
852
- }
853
691
  validate(throwError = !1) {
854
- let output = null;
855
- this.spaceId <= 0 ? output = "Note spaceId must be greater than zero." : !this.isNew && this.id <= 0 ? output = "Note id must be greater than zero if in non-new state." : this.ownTag && this.ownTag.spaceId != this.spaceId && (output = "Note cannot belong to a different space than its own tag");
856
- const survivingAttrs = this._attrs.filter((x) => !x.isDeleted);
692
+ function exit(message) {
693
+ if (throwError && message != null)
694
+ throw Error(message);
695
+ return message == null;
696
+ }
697
+ if (this.space) {
698
+ if (!this.isNew && this.id <= 0)
699
+ return exit("Note id must be greater than zero if in non-new state.");
700
+ if (this.ownTag && this.ownTag.space.id != this.space.id)
701
+ return exit("Note cannot belong to a different space than its own tag");
702
+ } else
703
+ return exit("Note must belong to a space.");
704
+ const survivingAttrs = this.attrs;
857
705
  for (let i = 0; i < survivingAttrs.length; i++) {
858
706
  const na = survivingAttrs[i];
859
707
  for (let j = i + 1; j < survivingAttrs.length; j++) {
860
708
  const na2 = survivingAttrs[j];
861
- na.attrId == na2.attrId && na.tagId == na2.tagId && (output = `Attr '${na.attr.name}' is duplicated.`);
709
+ if (na.attr.id == na2.attr.id)
710
+ return exit(`Attr '${na.attr.name}' is duplicated.`);
862
711
  }
863
712
  }
864
- if (throwError && output != null)
865
- throw Error(output);
866
713
  if (this.ownTag && !this.ownTag.validate(throwError))
867
714
  return !1;
868
715
  for (const nt of this._tags)
@@ -871,7 +718,160 @@ class Note extends ModelWithState {
871
718
  for (const na of this._attrs)
872
719
  if (!na.validate(throwError))
873
720
  return !1;
874
- return output == null;
721
+ return !0;
722
+ }
723
+ }
724
+ class NotuCache {
725
+ constructor(fetcher) {
726
+ __publicField(this, "_fetcher");
727
+ __publicField(this, "_spaces", null);
728
+ __publicField(this, "_tags", null);
729
+ __publicField(this, "_attrs", null);
730
+ if (!fetcher)
731
+ throw Error("NotuCache constructor must have a fetcher argument supplied.");
732
+ this._fetcher = fetcher;
733
+ }
734
+ async populate() {
735
+ await this._populateSpaces();
736
+ const tagsPromise = this._populateTags(), attrsPromise = this._populateAttrs();
737
+ await Promise.all([tagsPromise, attrsPromise]);
738
+ }
739
+ async _populateSpaces() {
740
+ const spacesData = await this._fetcher.getSpacesData();
741
+ this._spaces = /* @__PURE__ */ new Map();
742
+ for (const spaceData of spacesData) {
743
+ const space = this._spaceFromJSON(spaceData);
744
+ this._spaces.set(space.id, space);
745
+ }
746
+ }
747
+ _spaceFromJSON(spaceData) {
748
+ const space = new Space(spaceData.name);
749
+ return space.id = spaceData.id, space.version = spaceData.version, space.state = spaceData.state, space;
750
+ }
751
+ async _populateTags() {
752
+ const tagsData = await this._fetcher.getTagsData();
753
+ this._tags = /* @__PURE__ */ new Map();
754
+ for (const tagData of tagsData) {
755
+ const tag = this._tagFromJSON(tagData);
756
+ this._tags.set(tag.id, tag);
757
+ }
758
+ }
759
+ _tagFromJSON(tagData) {
760
+ const tag = new Tag(tagData.name);
761
+ return tag.id = tagData.id, tag.space = this._spaces.get(tagData.spaceId), tag.color = tagData.color, tag.isPublic = tagData.isPublic, tag.state = tagData.state, tag;
762
+ }
763
+ async _populateAttrs() {
764
+ const attrsData = await this._fetcher.getAttrsData();
765
+ this._attrs = /* @__PURE__ */ new Map();
766
+ for (const attrData of attrsData) {
767
+ const attr = this._attrFromJSON(attrData);
768
+ this._attrs.set(attr.id, attr);
769
+ }
770
+ }
771
+ _attrFromJSON(attrData) {
772
+ const attr = new Attr(attrData.name, attrData.description);
773
+ return attr.id = attrData.id, attr.type = attrData.type, attr.space = this._spaces.get(attrData.spaceId), attr.state = attrData.state, attr;
774
+ }
775
+ noteFromJSON(noteData) {
776
+ const note = new Note(noteData.text, this.getTag(noteData.id)).at(new Date(noteData.date)).in(this.getSpace(noteData.spaceId));
777
+ note.id = noteData.id, note.state = noteData.state;
778
+ for (const naData of noteData.attrs) {
779
+ const attr = this.getAttr(naData.attrId);
780
+ note.addAttr(attr, naData.value), note.getAttr(attr).state = naData.state;
781
+ }
782
+ for (const ntData of noteData.tags) {
783
+ const nt = note.addTag(this.getTag(ntData.tagId));
784
+ nt.state = ntData.state;
785
+ for (const ntaData of ntData.attrs) {
786
+ const attr = this.getAttr(ntaData.attrId);
787
+ nt.addAttr(attr, ntaData.value), nt.getAttr(attr).state = ntaData.state;
788
+ }
789
+ }
790
+ return note;
791
+ }
792
+ getSpaces() {
793
+ return Array.from(this._spaces.values());
794
+ }
795
+ getSpace(id) {
796
+ return this._spaces.get(id);
797
+ }
798
+ getSpaceByName(name) {
799
+ for (const space of this._spaces.values())
800
+ if (space.name == name)
801
+ return space;
802
+ }
803
+ spaceSaved(spaceData) {
804
+ const space = this._spaceFromJSON(spaceData);
805
+ return space.state == "DELETED" ? this._spaces.delete(space.id) : this._spaces.set(space.id, space), space;
806
+ }
807
+ getTags(space = null, includeOtherSpacePublics = !1) {
808
+ return space == null ? Array.from(this._tags.values()) : (space instanceof Space && (space = space.id), Array.from(this._tags.values()).filter((x) => x.isPublic && includeOtherSpacePublics || x.space.id == space));
809
+ }
810
+ getTag(id) {
811
+ return this._tags.get(id);
812
+ }
813
+ getTagByName(name, space) {
814
+ space instanceof Space && (space = space.id);
815
+ for (const tag of this._tags.values())
816
+ if (tag.name == name && tag.space.id == space)
817
+ return tag;
818
+ }
819
+ tagSaved(tagData) {
820
+ const tag = this._tagFromJSON(tagData);
821
+ return tag.state == "DELETED" ? this._tags.delete(tag.id) : this._tags.set(tag.id, tag), tag;
822
+ }
823
+ getAttrs(space = null) {
824
+ return space == null ? Array.from(this._attrs.values()) : (space instanceof Space && (space = space.id), Array.from(this._attrs.values()).filter((x) => x.space.id == space));
825
+ }
826
+ getAttr(id) {
827
+ return this._attrs.get(id);
828
+ }
829
+ getAttrByName(name, space) {
830
+ space instanceof Space && (space = space.id);
831
+ for (const attr of this._attrs.values())
832
+ if (attr.name == name && attr.space.id == space)
833
+ return attr;
834
+ }
835
+ attrSaved(attrData) {
836
+ const attr = this._attrFromJSON(attrData);
837
+ return attr.state == "DELETED" ? this._attrs.delete(attr.id) : this._attrs.set(attr.id, attr), attr;
838
+ }
839
+ }
840
+ class NotuHttpCacheFetcher {
841
+ constructor(url, token, fetchMethod = null) {
842
+ __publicField(this, "_url", null);
843
+ __publicField(this, "_token", null);
844
+ //Added for testing support
845
+ __publicField(this, "_fetch");
846
+ if (!url)
847
+ throw Error("Endpoint URL must be passed into NotuHttpCacheFetcher constructor");
848
+ if (!token)
849
+ throw Error("Security token must be passed into NotuHttpCacheFetcher constructor");
850
+ url.endsWith("/") && (url = url.substring(0, url.length - 1)), this._url = url, this._token = token, this._fetch = fetchMethod ?? window.fetch.bind(window);
851
+ }
852
+ get url() {
853
+ return this._url;
854
+ }
855
+ get token() {
856
+ return this._token;
857
+ }
858
+ async getSpacesData() {
859
+ return await this._getX("/spaces");
860
+ }
861
+ async getTagsData() {
862
+ return await this._getX("/tags");
863
+ }
864
+ async getAttrsData() {
865
+ return await this._getX("/attrs");
866
+ }
867
+ async _getX(endpoint) {
868
+ return await (await this._fetch(
869
+ this.url + endpoint,
870
+ {
871
+ method: "GET",
872
+ headers: { Authorization: "Bearer " + this.token }
873
+ }
874
+ )).json();
875
875
  }
876
876
  }
877
877
  class ParsedQuery {
@@ -956,11 +956,13 @@ function identifyAttrs(query, parsedQuery) {
956
956
  }
957
957
  export {
958
958
  Attr,
959
- CachedClient,
960
- HttpClient,
961
959
  Note,
962
960
  NoteAttr,
963
961
  NoteTag,
962
+ Notu,
963
+ NotuCache,
964
+ NotuHttpCacheFetcher,
965
+ NotuHttpClient,
964
966
  ParsedAttr,
965
967
  ParsedQuery,
966
968
  ParsedTag,