@beezping/adapter-memory 0.6.0 → 0.7.0
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/LICENSE +21 -0
- package/README.md +10 -8
- package/dist/beezping-core.d.cts +20 -0
- package/dist/beezping-core.d.ts +20 -0
- package/dist/concurrency.d.cts +14 -0
- package/dist/concurrency.d.ts +14 -0
- package/dist/constants/schema.d.cts +239 -0
- package/dist/constants/schema.d.ts +239 -0
- package/dist/deep-link.d.cts +14 -0
- package/dist/deep-link.d.ts +14 -0
- package/dist/email.d.cts +8 -5
- package/dist/email.d.ts +8 -5
- package/dist/errors.d.cts +28 -14
- package/dist/errors.d.ts +28 -14
- package/dist/filters.d.cts +15 -7
- package/dist/filters.d.ts +15 -7
- package/dist/i18n.d.cts +18 -0
- package/dist/i18n.d.ts +18 -0
- package/dist/index.cjs +99 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +99 -61
- package/dist/index.js.map +1 -1
- package/dist/schema.d.cts +12 -195
- package/dist/schema.d.ts +12 -195
- package/dist/screenshot-storage.d.cts +55 -30
- package/dist/screenshot-storage.d.ts +55 -30
- package/dist/store-helpers.d.cts +62 -34
- package/dist/store-helpers.d.ts +62 -34
- package/dist/type-utils.d.cts +8 -0
- package/dist/type-utils.d.ts +8 -0
- package/dist/types.d.cts +413 -154
- package/dist/types.d.ts +413 -154
- package/dist/wire.d.cts +42 -9
- package/dist/wire.d.ts +42 -9
- package/package.json +7 -8
- package/dist/siteping-core.d.cts +0 -17
- package/dist/siteping-core.d.ts +0 -17
package/dist/index.js
CHANGED
|
@@ -9,28 +9,21 @@ function clampPagination(query) {
|
|
|
9
9
|
const limit = Math.min(toPositiveInteger(query.limit, DEFAULT_PAGE_LIMIT), MAX_PAGE_LIMIT);
|
|
10
10
|
return { page, limit, skip: (page - 1) * limit };
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
12
|
+
function sortTime(record) {
|
|
13
|
+
const time = record.createdAt.getTime();
|
|
14
|
+
return Number.isNaN(time) ? Number.MIN_SAFE_INTEGER : time;
|
|
15
|
+
}
|
|
16
|
+
function matchesFeedbackQuery(record, query) {
|
|
17
|
+
const { type, status, statuses, url, urlPattern, search } = query;
|
|
18
|
+
return record.projectName === query.projectName && (!type || record.type === type) && // `statuses` (bucket / any-of) wins over the exact `status` filter when
|
|
19
|
+
// both are present; an empty array is treated as absent.
|
|
20
|
+
(statuses && statuses.length > 0 ? statuses.includes(record.status) : !status || record.status === status) && (!url || record.url === url) && (!urlPattern || record.urlPattern === urlPattern) && (!search || record.message.toLowerCase().includes(search.toLowerCase()));
|
|
14
21
|
}
|
|
15
22
|
function applyFeedbackFilters(items, query) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (query.statuses && query.statuses.length > 0) {
|
|
19
|
-
const allowed = query.statuses;
|
|
20
|
-
results = results.filter((f) => allowed.includes(f.status));
|
|
21
|
-
} else if (query.status) {
|
|
22
|
-
results = results.filter((f) => f.status === query.status);
|
|
23
|
-
}
|
|
24
|
-
if (query.url) results = results.filter((f) => f.url === query.url);
|
|
25
|
-
if (query.urlPattern) results = results.filter((f) => f.urlPattern === query.urlPattern);
|
|
26
|
-
if (query.search) {
|
|
27
|
-
const s = query.search.toLowerCase();
|
|
28
|
-
results = results.filter((f) => f.message.toLowerCase().includes(s));
|
|
29
|
-
}
|
|
30
|
-
results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
23
|
+
const results = items.filter((f) => matchesFeedbackQuery(f, query));
|
|
24
|
+
results.sort((a, b) => sortTime(b) - sortTime(a));
|
|
31
25
|
const total = results.length;
|
|
32
26
|
const { limit, skip } = clampPagination(query);
|
|
33
|
-
if (isUnreachableOffset(skip)) return { feedbacks: [], total };
|
|
34
27
|
return { feedbacks: results.slice(skip, skip + limit), total };
|
|
35
28
|
}
|
|
36
29
|
|
|
@@ -43,6 +36,7 @@ function hasOwn(value, key) {
|
|
|
43
36
|
}
|
|
44
37
|
|
|
45
38
|
// ../core/src/types.ts
|
|
39
|
+
var MAX_COMMENTS_PER_FEEDBACK = 100;
|
|
46
40
|
var StoreNotFoundError = class extends Error {
|
|
47
41
|
code = "STORE_NOT_FOUND";
|
|
48
42
|
constructor(message = "Record not found", options) {
|
|
@@ -64,6 +58,13 @@ var StorePersistenceError = class extends Error {
|
|
|
64
58
|
this.name = "StorePersistenceError";
|
|
65
59
|
}
|
|
66
60
|
};
|
|
61
|
+
var StoreLimitError = class extends Error {
|
|
62
|
+
code = "STORE_LIMIT";
|
|
63
|
+
constructor(message = "Store limit reached", options) {
|
|
64
|
+
super(message, options);
|
|
65
|
+
this.name = "StoreLimitError";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
67
68
|
function hasErrorCode(error, code) {
|
|
68
69
|
return hasOwn(error, "code") && error.code === code;
|
|
69
70
|
}
|
|
@@ -99,6 +100,18 @@ function buildAnnotationRecord(input, ctx) {
|
|
|
99
100
|
createdAt: ctx.now
|
|
100
101
|
};
|
|
101
102
|
}
|
|
103
|
+
function buildCommentRecord(input, ctx) {
|
|
104
|
+
return {
|
|
105
|
+
id: ctx.id,
|
|
106
|
+
feedbackId: ctx.feedbackId,
|
|
107
|
+
body: input.body,
|
|
108
|
+
authorName: input.authorName,
|
|
109
|
+
authorEmail: input.authorEmail,
|
|
110
|
+
authorRole: input.authorRole,
|
|
111
|
+
clientId: input.clientId,
|
|
112
|
+
createdAt: ctx.now ?? /* @__PURE__ */ new Date()
|
|
113
|
+
};
|
|
114
|
+
}
|
|
102
115
|
function buildFeedbackRecord(input, ctx) {
|
|
103
116
|
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
104
117
|
return {
|
|
@@ -126,23 +139,24 @@ function buildFeedbackRecord(input, ctx) {
|
|
|
126
139
|
};
|
|
127
140
|
}
|
|
128
141
|
function createCollectionStore(backend) {
|
|
129
|
-
let
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
()
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
let tail = Promise.resolve();
|
|
143
|
+
function mutate(mutation) {
|
|
144
|
+
const result = tail.then(() => {
|
|
145
|
+
const loaded = backend.load();
|
|
146
|
+
return Array.isArray(loaded) ? mutation(loaded) : loaded.then(mutation);
|
|
147
|
+
});
|
|
148
|
+
tail = result.catch(() => {
|
|
149
|
+
});
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
const createFeedbackIfAbsent = (data) => mutate(async (feedbacks) => {
|
|
140
153
|
const existing = feedbacks.find((f) => f.clientId === data.clientId);
|
|
141
154
|
if (existing) return { feedback: existing, created: false };
|
|
142
155
|
const record = buildFeedbackRecord(data, {
|
|
143
156
|
id: backend.generateId(),
|
|
144
157
|
annotationId: () => backend.generateId()
|
|
145
158
|
});
|
|
159
|
+
if (backend.comments) record.comments = [];
|
|
146
160
|
const next = [record, ...feedbacks];
|
|
147
161
|
try {
|
|
148
162
|
await backend.persist(next);
|
|
@@ -152,9 +166,8 @@ function createCollectionStore(backend) {
|
|
|
152
166
|
await backend.persist(next);
|
|
153
167
|
}
|
|
154
168
|
return { feedback: record, created: true };
|
|
155
|
-
};
|
|
156
|
-
const
|
|
157
|
-
return {
|
|
169
|
+
});
|
|
170
|
+
const store = {
|
|
158
171
|
createFeedbackIfAbsent,
|
|
159
172
|
async createFeedback(data) {
|
|
160
173
|
return (await createFeedbackIfAbsent(data)).feedback;
|
|
@@ -165,39 +178,55 @@ function createCollectionStore(backend) {
|
|
|
165
178
|
async findByClientId(clientId) {
|
|
166
179
|
return (await backend.load()).find((f) => f.clientId === clientId) ?? null;
|
|
167
180
|
},
|
|
168
|
-
updateFeedback(id, data) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
await backend.persist(feedbacks.filter((f) => f.id !== id));
|
|
188
|
-
});
|
|
189
|
-
},
|
|
190
|
-
deleteAllFeedbacks(projectName) {
|
|
191
|
-
return runSerializedMutation(async () => {
|
|
192
|
-
const feedbacks = await backend.load();
|
|
193
|
-
await backend.persist(feedbacks.filter((f) => f.projectName !== projectName));
|
|
194
|
-
});
|
|
195
|
-
},
|
|
181
|
+
updateFeedback: (id, data) => mutate(async (feedbacks) => {
|
|
182
|
+
const current = feedbacks.find((f) => f.id === id);
|
|
183
|
+
if (!current) throw new StoreNotFoundError();
|
|
184
|
+
const updated = {
|
|
185
|
+
...current,
|
|
186
|
+
status: data.status,
|
|
187
|
+
resolvedAt: data.resolvedAt,
|
|
188
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
189
|
+
};
|
|
190
|
+
await backend.persist(feedbacks.map((f) => f === current ? updated : f));
|
|
191
|
+
return updated;
|
|
192
|
+
}),
|
|
193
|
+
deleteFeedback: (id) => mutate(async (feedbacks) => {
|
|
194
|
+
if (!feedbacks.some((f) => f.id === id)) throw new StoreNotFoundError();
|
|
195
|
+
await backend.persist(feedbacks.filter((f) => f.id !== id));
|
|
196
|
+
}),
|
|
197
|
+
deleteAllFeedbacks: (projectName) => mutate(async (feedbacks) => {
|
|
198
|
+
await backend.persist(feedbacks.filter((f) => f.projectName !== projectName));
|
|
199
|
+
}),
|
|
196
200
|
async verifyProjectOwnership(id, projectName) {
|
|
197
201
|
const fb = (await backend.load()).find((f) => f.id === id);
|
|
198
202
|
return fb !== void 0 && fb.projectName === projectName;
|
|
199
203
|
}
|
|
200
204
|
};
|
|
205
|
+
if (!backend.comments) return store;
|
|
206
|
+
return {
|
|
207
|
+
...store,
|
|
208
|
+
addComment: (feedbackId, data) => mutate(async (feedbacks) => {
|
|
209
|
+
const existing = feedbacks.flatMap((f) => f.comments ?? []).find((c) => c.clientId === data.clientId);
|
|
210
|
+
if (existing) return existing;
|
|
211
|
+
const current = feedbacks.find((f) => f.id === feedbackId);
|
|
212
|
+
if (!current) throw new StoreNotFoundError();
|
|
213
|
+
const thread = current.comments ?? [];
|
|
214
|
+
if (data.authorRole === "client" && thread.filter((c) => c.authorRole === "client").length >= MAX_COMMENTS_PER_FEEDBACK) {
|
|
215
|
+
throw new StoreLimitError(`A thread holds at most ${MAX_COMMENTS_PER_FEEDBACK} client comments`);
|
|
216
|
+
}
|
|
217
|
+
const comment = buildCommentRecord(data, { id: backend.generateId(), feedbackId });
|
|
218
|
+
const updated = { ...current, comments: [...thread, comment] };
|
|
219
|
+
await backend.persist(feedbacks.map((f) => f === current ? updated : f));
|
|
220
|
+
return comment;
|
|
221
|
+
}),
|
|
222
|
+
deleteComment: (feedbackId, commentId) => mutate(async (feedbacks) => {
|
|
223
|
+
const current = feedbacks.find((f) => f.id === feedbackId);
|
|
224
|
+
const thread = current?.comments ?? [];
|
|
225
|
+
if (!current || !thread.some((c) => c.id === commentId)) throw new StoreNotFoundError();
|
|
226
|
+
const updated = { ...current, comments: thread.filter((c) => c.id !== commentId) };
|
|
227
|
+
await backend.persist(feedbacks.map((f) => f === current ? updated : f));
|
|
228
|
+
})
|
|
229
|
+
};
|
|
201
230
|
}
|
|
202
231
|
|
|
203
232
|
// src/index.ts
|
|
@@ -209,7 +238,9 @@ var MemoryStore = class {
|
|
|
209
238
|
persist: (next) => {
|
|
210
239
|
this.feedbacks = next;
|
|
211
240
|
},
|
|
212
|
-
generateId: () => `mem-${this.idCounter++}-${Date.now().toString(36)}
|
|
241
|
+
generateId: () => `mem-${this.idCounter++}-${Date.now().toString(36)}`,
|
|
242
|
+
// Records live in memory: comment dates stay Dates.
|
|
243
|
+
comments: true
|
|
213
244
|
});
|
|
214
245
|
createFeedback(data) {
|
|
215
246
|
return this.engine.createFeedback(data);
|
|
@@ -235,6 +266,12 @@ var MemoryStore = class {
|
|
|
235
266
|
verifyProjectOwnership(id, projectName) {
|
|
236
267
|
return this.engine.verifyProjectOwnership(id, projectName);
|
|
237
268
|
}
|
|
269
|
+
addComment(feedbackId, data) {
|
|
270
|
+
return this.engine.addComment(feedbackId, data);
|
|
271
|
+
}
|
|
272
|
+
deleteComment(feedbackId, commentId) {
|
|
273
|
+
return this.engine.deleteComment(feedbackId, commentId);
|
|
274
|
+
}
|
|
238
275
|
/** Remove all data from this store instance. */
|
|
239
276
|
clear() {
|
|
240
277
|
this.feedbacks = [];
|
|
@@ -244,6 +281,7 @@ var MemoryStore = class {
|
|
|
244
281
|
export {
|
|
245
282
|
MemoryStore,
|
|
246
283
|
StoreDuplicateError,
|
|
284
|
+
StoreLimitError,
|
|
247
285
|
StoreNotFoundError,
|
|
248
286
|
StorePersistenceError,
|
|
249
287
|
isStorePersistence
|