kiro-memory 1.5.0 → 1.6.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.
@@ -1,14 +1,12 @@
1
1
  import { createRequire } from 'module';const require = createRequire(import.meta.url);
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __esm = (fn, res) => function __init() {
7
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
- };
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
10
  };
13
11
  var __copyProps = (to, from, except, desc) => {
14
12
  if (from && typeof from === "object" || typeof from === "function") {
@@ -18,267 +16,2993 @@ var __copyProps = (to, from, except, desc) => {
18
16
  }
19
17
  return to;
20
18
  };
21
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
22
27
 
23
- // src/services/sqlite/Observations.ts
24
- var Observations_exports = {};
25
- __export(Observations_exports, {
26
- createObservation: () => createObservation,
27
- deleteObservation: () => deleteObservation,
28
- getObservationsByProject: () => getObservationsByProject,
29
- getObservationsBySession: () => getObservationsBySession,
30
- searchObservations: () => searchObservations
28
+ // node_modules/ip-address/dist/common.js
29
+ var require_common = __commonJS({
30
+ "node_modules/ip-address/dist/common.js"(exports) {
31
+ "use strict";
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.isInSubnet = isInSubnet;
34
+ exports.isCorrect = isCorrect;
35
+ exports.numberToPaddedHex = numberToPaddedHex;
36
+ exports.stringToPaddedHex = stringToPaddedHex;
37
+ exports.testBit = testBit;
38
+ function isInSubnet(address) {
39
+ if (this.subnetMask < address.subnetMask) {
40
+ return false;
41
+ }
42
+ if (this.mask(address.subnetMask) === address.mask()) {
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ function isCorrect(defaultBits) {
48
+ return function() {
49
+ if (this.addressMinusSuffix !== this.correctForm()) {
50
+ return false;
51
+ }
52
+ if (this.subnetMask === defaultBits && !this.parsedSubnet) {
53
+ return true;
54
+ }
55
+ return this.parsedSubnet === String(this.subnetMask);
56
+ };
57
+ }
58
+ function numberToPaddedHex(number) {
59
+ return number.toString(16).padStart(2, "0");
60
+ }
61
+ function stringToPaddedHex(numberString) {
62
+ return numberToPaddedHex(parseInt(numberString, 10));
63
+ }
64
+ function testBit(binaryValue, position) {
65
+ const { length } = binaryValue;
66
+ if (position > length) {
67
+ return false;
68
+ }
69
+ const positionInString = length - position;
70
+ return binaryValue.substring(positionInString, positionInString + 1) === "1";
71
+ }
72
+ }
31
73
  });
32
- function createObservation(db2, memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber) {
33
- const now = /* @__PURE__ */ new Date();
34
- const result = db2.run(
35
- `INSERT INTO observations
36
- (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch)
37
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
38
- [memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime()]
39
- );
40
- return Number(result.lastInsertRowid);
41
- }
42
- function getObservationsBySession(db2, memorySessionId) {
43
- const query = db2.query(
44
- "SELECT * FROM observations WHERE memory_session_id = ? ORDER BY prompt_number ASC"
45
- );
46
- return query.all(memorySessionId);
47
- }
48
- function getObservationsByProject(db2, project, limit = 100) {
49
- const query = db2.query(
50
- "SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
51
- );
52
- return query.all(project, limit);
53
- }
54
- function searchObservations(db2, searchTerm, project) {
55
- const sql = project ? `SELECT * FROM observations
56
- WHERE project = ? AND (title LIKE ? OR text LIKE ? OR narrative LIKE ?)
57
- ORDER BY created_at_epoch DESC` : `SELECT * FROM observations
58
- WHERE title LIKE ? OR text LIKE ? OR narrative LIKE ?
59
- ORDER BY created_at_epoch DESC`;
60
- const pattern = `%${searchTerm}%`;
61
- const query = db2.query(sql);
62
- if (project) {
63
- return query.all(project, pattern, pattern, pattern);
64
- }
65
- return query.all(pattern, pattern, pattern);
66
- }
67
- function deleteObservation(db2, id) {
68
- db2.run("DELETE FROM observations WHERE id = ?", [id]);
69
- }
70
- var init_Observations = __esm({
71
- "src/services/sqlite/Observations.ts"() {
74
+
75
+ // node_modules/ip-address/dist/v4/constants.js
76
+ var require_constants = __commonJS({
77
+ "node_modules/ip-address/dist/v4/constants.js"(exports) {
72
78
  "use strict";
79
+ Object.defineProperty(exports, "__esModule", { value: true });
80
+ exports.RE_SUBNET_STRING = exports.RE_ADDRESS = exports.GROUPS = exports.BITS = void 0;
81
+ exports.BITS = 32;
82
+ exports.GROUPS = 4;
83
+ exports.RE_ADDRESS = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;
84
+ exports.RE_SUBNET_STRING = /\/\d{1,2}$/;
73
85
  }
74
86
  });
75
87
 
76
- // src/services/sqlite/Summaries.ts
77
- var Summaries_exports = {};
78
- __export(Summaries_exports, {
79
- createSummary: () => createSummary,
80
- deleteSummary: () => deleteSummary,
81
- getSummariesByProject: () => getSummariesByProject,
82
- getSummaryBySession: () => getSummaryBySession,
83
- searchSummaries: () => searchSummaries
88
+ // node_modules/ip-address/dist/address-error.js
89
+ var require_address_error = __commonJS({
90
+ "node_modules/ip-address/dist/address-error.js"(exports) {
91
+ "use strict";
92
+ Object.defineProperty(exports, "__esModule", { value: true });
93
+ exports.AddressError = void 0;
94
+ var AddressError = class extends Error {
95
+ constructor(message, parseMessage) {
96
+ super(message);
97
+ this.name = "AddressError";
98
+ this.parseMessage = parseMessage;
99
+ }
100
+ };
101
+ exports.AddressError = AddressError;
102
+ }
84
103
  });
85
- function createSummary(db2, sessionId, project, request, investigated, learned, completed, nextSteps, notes) {
86
- const now = /* @__PURE__ */ new Date();
87
- const result = db2.run(
88
- `INSERT INTO summaries
89
- (session_id, project, request, investigated, learned, completed, next_steps, notes, created_at, created_at_epoch)
90
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
91
- [sessionId, project, request, investigated, learned, completed, nextSteps, notes, now.toISOString(), now.getTime()]
92
- );
93
- return Number(result.lastInsertRowid);
94
- }
95
- function getSummaryBySession(db2, sessionId) {
96
- const query = db2.query("SELECT * FROM summaries WHERE session_id = ? ORDER BY created_at_epoch DESC LIMIT 1");
97
- return query.get(sessionId);
98
- }
99
- function getSummariesByProject(db2, project, limit = 50) {
100
- const query = db2.query(
101
- "SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
102
- );
103
- return query.all(project, limit);
104
- }
105
- function searchSummaries(db2, searchTerm, project) {
106
- const sql = project ? `SELECT * FROM summaries
107
- WHERE project = ? AND (request LIKE ? OR learned LIKE ? OR completed LIKE ? OR notes LIKE ?)
108
- ORDER BY created_at_epoch DESC` : `SELECT * FROM summaries
109
- WHERE request LIKE ? OR learned LIKE ? OR completed LIKE ? OR notes LIKE ?
110
- ORDER BY created_at_epoch DESC`;
111
- const pattern = `%${searchTerm}%`;
112
- const query = db2.query(sql);
113
- if (project) {
114
- return query.all(project, pattern, pattern, pattern, pattern);
115
- }
116
- return query.all(pattern, pattern, pattern, pattern);
117
- }
118
- function deleteSummary(db2, id) {
119
- db2.run("DELETE FROM summaries WHERE id = ?", [id]);
120
- }
121
- var init_Summaries = __esm({
122
- "src/services/sqlite/Summaries.ts"() {
104
+
105
+ // node_modules/ip-address/dist/ipv4.js
106
+ var require_ipv4 = __commonJS({
107
+ "node_modules/ip-address/dist/ipv4.js"(exports) {
123
108
  "use strict";
109
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
110
+ if (k2 === void 0) k2 = k;
111
+ var desc = Object.getOwnPropertyDescriptor(m, k);
112
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
113
+ desc = { enumerable: true, get: function() {
114
+ return m[k];
115
+ } };
116
+ }
117
+ Object.defineProperty(o, k2, desc);
118
+ }) : (function(o, m, k, k2) {
119
+ if (k2 === void 0) k2 = k;
120
+ o[k2] = m[k];
121
+ }));
122
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
123
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
124
+ }) : function(o, v) {
125
+ o["default"] = v;
126
+ });
127
+ var __importStar = exports && exports.__importStar || function(mod) {
128
+ if (mod && mod.__esModule) return mod;
129
+ var result = {};
130
+ if (mod != null) {
131
+ for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
132
+ }
133
+ __setModuleDefault(result, mod);
134
+ return result;
135
+ };
136
+ Object.defineProperty(exports, "__esModule", { value: true });
137
+ exports.Address4 = void 0;
138
+ var common = __importStar(require_common());
139
+ var constants = __importStar(require_constants());
140
+ var address_error_1 = require_address_error();
141
+ var Address4 = class _Address4 {
142
+ constructor(address) {
143
+ this.groups = constants.GROUPS;
144
+ this.parsedAddress = [];
145
+ this.parsedSubnet = "";
146
+ this.subnet = "/32";
147
+ this.subnetMask = 32;
148
+ this.v4 = true;
149
+ this.isCorrect = common.isCorrect(constants.BITS);
150
+ this.isInSubnet = common.isInSubnet;
151
+ this.address = address;
152
+ const subnet = constants.RE_SUBNET_STRING.exec(address);
153
+ if (subnet) {
154
+ this.parsedSubnet = subnet[0].replace("/", "");
155
+ this.subnetMask = parseInt(this.parsedSubnet, 10);
156
+ this.subnet = `/${this.subnetMask}`;
157
+ if (this.subnetMask < 0 || this.subnetMask > constants.BITS) {
158
+ throw new address_error_1.AddressError("Invalid subnet mask.");
159
+ }
160
+ address = address.replace(constants.RE_SUBNET_STRING, "");
161
+ }
162
+ this.addressMinusSuffix = address;
163
+ this.parsedAddress = this.parse(address);
164
+ }
165
+ static isValid(address) {
166
+ try {
167
+ new _Address4(address);
168
+ return true;
169
+ } catch (e) {
170
+ return false;
171
+ }
172
+ }
173
+ /*
174
+ * Parses a v4 address
175
+ */
176
+ parse(address) {
177
+ const groups = address.split(".");
178
+ if (!address.match(constants.RE_ADDRESS)) {
179
+ throw new address_error_1.AddressError("Invalid IPv4 address.");
180
+ }
181
+ return groups;
182
+ }
183
+ /**
184
+ * Returns the correct form of an address
185
+ * @memberof Address4
186
+ * @instance
187
+ * @returns {String}
188
+ */
189
+ correctForm() {
190
+ return this.parsedAddress.map((part) => parseInt(part, 10)).join(".");
191
+ }
192
+ /**
193
+ * Converts a hex string to an IPv4 address object
194
+ * @memberof Address4
195
+ * @static
196
+ * @param {string} hex - a hex string to convert
197
+ * @returns {Address4}
198
+ */
199
+ static fromHex(hex) {
200
+ const padded = hex.replace(/:/g, "").padStart(8, "0");
201
+ const groups = [];
202
+ let i;
203
+ for (i = 0; i < 8; i += 2) {
204
+ const h = padded.slice(i, i + 2);
205
+ groups.push(parseInt(h, 16));
206
+ }
207
+ return new _Address4(groups.join("."));
208
+ }
209
+ /**
210
+ * Converts an integer into a IPv4 address object
211
+ * @memberof Address4
212
+ * @static
213
+ * @param {integer} integer - a number to convert
214
+ * @returns {Address4}
215
+ */
216
+ static fromInteger(integer) {
217
+ return _Address4.fromHex(integer.toString(16));
218
+ }
219
+ /**
220
+ * Return an address from in-addr.arpa form
221
+ * @memberof Address4
222
+ * @static
223
+ * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
224
+ * @returns {Adress4}
225
+ * @example
226
+ * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
227
+ * address.correctForm(); // '192.0.2.42'
228
+ */
229
+ static fromArpa(arpaFormAddress) {
230
+ const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, "");
231
+ const address = leader.split(".").reverse().join(".");
232
+ return new _Address4(address);
233
+ }
234
+ /**
235
+ * Converts an IPv4 address object to a hex string
236
+ * @memberof Address4
237
+ * @instance
238
+ * @returns {String}
239
+ */
240
+ toHex() {
241
+ return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(":");
242
+ }
243
+ /**
244
+ * Converts an IPv4 address object to an array of bytes
245
+ * @memberof Address4
246
+ * @instance
247
+ * @returns {Array}
248
+ */
249
+ toArray() {
250
+ return this.parsedAddress.map((part) => parseInt(part, 10));
251
+ }
252
+ /**
253
+ * Converts an IPv4 address object to an IPv6 address group
254
+ * @memberof Address4
255
+ * @instance
256
+ * @returns {String}
257
+ */
258
+ toGroup6() {
259
+ const output = [];
260
+ let i;
261
+ for (i = 0; i < constants.GROUPS; i += 2) {
262
+ output.push(`${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(this.parsedAddress[i + 1])}`);
263
+ }
264
+ return output.join(":");
265
+ }
266
+ /**
267
+ * Returns the address as a `bigint`
268
+ * @memberof Address4
269
+ * @instance
270
+ * @returns {bigint}
271
+ */
272
+ bigInt() {
273
+ return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join("")}`);
274
+ }
275
+ /**
276
+ * Helper function getting start address.
277
+ * @memberof Address4
278
+ * @instance
279
+ * @returns {bigint}
280
+ */
281
+ _startAddress() {
282
+ return BigInt(`0b${this.mask() + "0".repeat(constants.BITS - this.subnetMask)}`);
283
+ }
284
+ /**
285
+ * The first address in the range given by this address' subnet.
286
+ * Often referred to as the Network Address.
287
+ * @memberof Address4
288
+ * @instance
289
+ * @returns {Address4}
290
+ */
291
+ startAddress() {
292
+ return _Address4.fromBigInt(this._startAddress());
293
+ }
294
+ /**
295
+ * The first host address in the range given by this address's subnet ie
296
+ * the first address after the Network Address
297
+ * @memberof Address4
298
+ * @instance
299
+ * @returns {Address4}
300
+ */
301
+ startAddressExclusive() {
302
+ const adjust = BigInt("1");
303
+ return _Address4.fromBigInt(this._startAddress() + adjust);
304
+ }
305
+ /**
306
+ * Helper function getting end address.
307
+ * @memberof Address4
308
+ * @instance
309
+ * @returns {bigint}
310
+ */
311
+ _endAddress() {
312
+ return BigInt(`0b${this.mask() + "1".repeat(constants.BITS - this.subnetMask)}`);
313
+ }
314
+ /**
315
+ * The last address in the range given by this address' subnet
316
+ * Often referred to as the Broadcast
317
+ * @memberof Address4
318
+ * @instance
319
+ * @returns {Address4}
320
+ */
321
+ endAddress() {
322
+ return _Address4.fromBigInt(this._endAddress());
323
+ }
324
+ /**
325
+ * The last host address in the range given by this address's subnet ie
326
+ * the last address prior to the Broadcast Address
327
+ * @memberof Address4
328
+ * @instance
329
+ * @returns {Address4}
330
+ */
331
+ endAddressExclusive() {
332
+ const adjust = BigInt("1");
333
+ return _Address4.fromBigInt(this._endAddress() - adjust);
334
+ }
335
+ /**
336
+ * Converts a BigInt to a v4 address object
337
+ * @memberof Address4
338
+ * @static
339
+ * @param {bigint} bigInt - a BigInt to convert
340
+ * @returns {Address4}
341
+ */
342
+ static fromBigInt(bigInt) {
343
+ return _Address4.fromHex(bigInt.toString(16));
344
+ }
345
+ /**
346
+ * Returns the first n bits of the address, defaulting to the
347
+ * subnet mask
348
+ * @memberof Address4
349
+ * @instance
350
+ * @returns {String}
351
+ */
352
+ mask(mask) {
353
+ if (mask === void 0) {
354
+ mask = this.subnetMask;
355
+ }
356
+ return this.getBitsBase2(0, mask);
357
+ }
358
+ /**
359
+ * Returns the bits in the given range as a base-2 string
360
+ * @memberof Address4
361
+ * @instance
362
+ * @returns {string}
363
+ */
364
+ getBitsBase2(start, end) {
365
+ return this.binaryZeroPad().slice(start, end);
366
+ }
367
+ /**
368
+ * Return the reversed ip6.arpa form of the address
369
+ * @memberof Address4
370
+ * @param {Object} options
371
+ * @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
372
+ * @instance
373
+ * @returns {String}
374
+ */
375
+ reverseForm(options) {
376
+ if (!options) {
377
+ options = {};
378
+ }
379
+ const reversed = this.correctForm().split(".").reverse().join(".");
380
+ if (options.omitSuffix) {
381
+ return reversed;
382
+ }
383
+ return `${reversed}.in-addr.arpa.`;
384
+ }
385
+ /**
386
+ * Returns true if the given address is a multicast address
387
+ * @memberof Address4
388
+ * @instance
389
+ * @returns {boolean}
390
+ */
391
+ isMulticast() {
392
+ return this.isInSubnet(new _Address4("224.0.0.0/4"));
393
+ }
394
+ /**
395
+ * Returns a zero-padded base-2 string representation of the address
396
+ * @memberof Address4
397
+ * @instance
398
+ * @returns {string}
399
+ */
400
+ binaryZeroPad() {
401
+ return this.bigInt().toString(2).padStart(constants.BITS, "0");
402
+ }
403
+ /**
404
+ * Groups an IPv4 address for inclusion at the end of an IPv6 address
405
+ * @returns {String}
406
+ */
407
+ groupForV6() {
408
+ const segments = this.parsedAddress;
409
+ return this.address.replace(constants.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments.slice(0, 2).join(".")}</span>.<span class="hover-group group-v4 group-7">${segments.slice(2, 4).join(".")}</span>`);
410
+ }
411
+ };
412
+ exports.Address4 = Address4;
124
413
  }
125
414
  });
126
415
 
127
- // src/services/sqlite/Search.ts
128
- var Search_exports = {};
129
- __export(Search_exports, {
130
- getObservationsByIds: () => getObservationsByIds,
131
- getProjectStats: () => getProjectStats,
132
- getTimeline: () => getTimeline,
133
- searchObservationsFTS: () => searchObservationsFTS,
134
- searchObservationsLIKE: () => searchObservationsLIKE,
135
- searchSummariesFiltered: () => searchSummariesFiltered
416
+ // node_modules/ip-address/dist/v6/constants.js
417
+ var require_constants2 = __commonJS({
418
+ "node_modules/ip-address/dist/v6/constants.js"(exports) {
419
+ "use strict";
420
+ Object.defineProperty(exports, "__esModule", { value: true });
421
+ exports.RE_URL_WITH_PORT = exports.RE_URL = exports.RE_ZONE_STRING = exports.RE_SUBNET_STRING = exports.RE_BAD_ADDRESS = exports.RE_BAD_CHARACTERS = exports.TYPES = exports.SCOPES = exports.GROUPS = exports.BITS = void 0;
422
+ exports.BITS = 128;
423
+ exports.GROUPS = 8;
424
+ exports.SCOPES = {
425
+ 0: "Reserved",
426
+ 1: "Interface local",
427
+ 2: "Link local",
428
+ 4: "Admin local",
429
+ 5: "Site local",
430
+ 8: "Organization local",
431
+ 14: "Global",
432
+ 15: "Reserved"
433
+ };
434
+ exports.TYPES = {
435
+ "ff01::1/128": "Multicast (All nodes on this interface)",
436
+ "ff01::2/128": "Multicast (All routers on this interface)",
437
+ "ff02::1/128": "Multicast (All nodes on this link)",
438
+ "ff02::2/128": "Multicast (All routers on this link)",
439
+ "ff05::2/128": "Multicast (All routers in this site)",
440
+ "ff02::5/128": "Multicast (OSPFv3 AllSPF routers)",
441
+ "ff02::6/128": "Multicast (OSPFv3 AllDR routers)",
442
+ "ff02::9/128": "Multicast (RIP routers)",
443
+ "ff02::a/128": "Multicast (EIGRP routers)",
444
+ "ff02::d/128": "Multicast (PIM routers)",
445
+ "ff02::16/128": "Multicast (MLDv2 reports)",
446
+ "ff01::fb/128": "Multicast (mDNSv6)",
447
+ "ff02::fb/128": "Multicast (mDNSv6)",
448
+ "ff05::fb/128": "Multicast (mDNSv6)",
449
+ "ff02::1:2/128": "Multicast (All DHCP servers and relay agents on this link)",
450
+ "ff05::1:2/128": "Multicast (All DHCP servers and relay agents in this site)",
451
+ "ff02::1:3/128": "Multicast (All DHCP servers on this link)",
452
+ "ff05::1:3/128": "Multicast (All DHCP servers in this site)",
453
+ "::/128": "Unspecified",
454
+ "::1/128": "Loopback",
455
+ "ff00::/8": "Multicast",
456
+ "fe80::/10": "Link-local unicast"
457
+ };
458
+ exports.RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi;
459
+ exports.RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\/$)/gi;
460
+ exports.RE_SUBNET_STRING = /\/\d{1,3}(?=%|$)/;
461
+ exports.RE_ZONE_STRING = /%.*$/;
462
+ exports.RE_URL = /^\[{0,1}([0-9a-f:]+)\]{0,1}/;
463
+ exports.RE_URL_WITH_PORT = /\[([0-9a-f:]+)\]:([0-9]{1,5})/;
464
+ }
136
465
  });
137
- function searchObservationsFTS(db2, query, filters = {}) {
138
- const limit = filters.limit || 50;
139
- try {
140
- let sql = `
141
- SELECT o.* FROM observations o
142
- JOIN observations_fts fts ON o.id = fts.rowid
143
- WHERE observations_fts MATCH ?
144
- `;
145
- const params = [query];
146
- if (filters.project) {
147
- sql += " AND o.project = ?";
148
- params.push(filters.project);
466
+
467
+ // node_modules/ip-address/dist/v6/helpers.js
468
+ var require_helpers = __commonJS({
469
+ "node_modules/ip-address/dist/v6/helpers.js"(exports) {
470
+ "use strict";
471
+ Object.defineProperty(exports, "__esModule", { value: true });
472
+ exports.spanAllZeroes = spanAllZeroes;
473
+ exports.spanAll = spanAll;
474
+ exports.spanLeadingZeroes = spanLeadingZeroes;
475
+ exports.simpleGroup = simpleGroup;
476
+ function spanAllZeroes(s) {
477
+ return s.replace(/(0+)/g, '<span class="zero">$1</span>');
149
478
  }
150
- if (filters.type) {
151
- sql += " AND o.type = ?";
152
- params.push(filters.type);
479
+ function spanAll(s, offset = 0) {
480
+ const letters = s.split("");
481
+ return letters.map((n, i) => `<span class="digit value-${n} position-${i + offset}">${spanAllZeroes(n)}</span>`).join("");
153
482
  }
154
- if (filters.dateStart) {
155
- sql += " AND o.created_at_epoch >= ?";
156
- params.push(filters.dateStart);
483
+ function spanLeadingZeroesSimple(group) {
484
+ return group.replace(/^(0+)/, '<span class="zero">$1</span>');
157
485
  }
158
- if (filters.dateEnd) {
159
- sql += " AND o.created_at_epoch <= ?";
160
- params.push(filters.dateEnd);
486
+ function spanLeadingZeroes(address) {
487
+ const groups = address.split(":");
488
+ return groups.map((g) => spanLeadingZeroesSimple(g)).join(":");
489
+ }
490
+ function simpleGroup(addressString, offset = 0) {
491
+ const groups = addressString.split(":");
492
+ return groups.map((g, i) => {
493
+ if (/group-v4/.test(g)) {
494
+ return g;
495
+ }
496
+ return `<span class="hover-group group-${i + offset}">${spanLeadingZeroesSimple(g)}</span>`;
497
+ });
161
498
  }
162
- sql += " ORDER BY rank LIMIT ?";
163
- params.push(limit);
164
- const stmt = db2.query(sql);
165
- return stmt.all(...params);
166
- } catch {
167
- return searchObservationsLIKE(db2, query, filters);
168
- }
169
- }
170
- function searchObservationsLIKE(db2, query, filters = {}) {
171
- const limit = filters.limit || 50;
172
- const pattern = `%${query}%`;
173
- let sql = `
174
- SELECT * FROM observations
175
- WHERE (title LIKE ? OR text LIKE ? OR narrative LIKE ? OR concepts LIKE ?)
176
- `;
177
- const params = [pattern, pattern, pattern, pattern];
178
- if (filters.project) {
179
- sql += " AND project = ?";
180
- params.push(filters.project);
181
- }
182
- if (filters.type) {
183
- sql += " AND type = ?";
184
- params.push(filters.type);
185
- }
186
- if (filters.dateStart) {
187
- sql += " AND created_at_epoch >= ?";
188
- params.push(filters.dateStart);
189
- }
190
- if (filters.dateEnd) {
191
- sql += " AND created_at_epoch <= ?";
192
- params.push(filters.dateEnd);
193
- }
194
- sql += " ORDER BY created_at_epoch DESC LIMIT ?";
195
- params.push(limit);
196
- const stmt = db2.query(sql);
197
- return stmt.all(...params);
198
- }
199
- function searchSummariesFiltered(db2, query, filters = {}) {
200
- const limit = filters.limit || 20;
201
- const pattern = `%${query}%`;
202
- let sql = `
203
- SELECT * FROM summaries
204
- WHERE (request LIKE ? OR learned LIKE ? OR completed LIKE ? OR notes LIKE ? OR next_steps LIKE ?)
205
- `;
206
- const params = [pattern, pattern, pattern, pattern, pattern];
207
- if (filters.project) {
208
- sql += " AND project = ?";
209
- params.push(filters.project);
210
- }
211
- if (filters.dateStart) {
212
- sql += " AND created_at_epoch >= ?";
213
- params.push(filters.dateStart);
214
499
  }
215
- if (filters.dateEnd) {
216
- sql += " AND created_at_epoch <= ?";
217
- params.push(filters.dateEnd);
500
+ });
501
+
502
+ // node_modules/ip-address/dist/v6/regular-expressions.js
503
+ var require_regular_expressions = __commonJS({
504
+ "node_modules/ip-address/dist/v6/regular-expressions.js"(exports) {
505
+ "use strict";
506
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
507
+ if (k2 === void 0) k2 = k;
508
+ var desc = Object.getOwnPropertyDescriptor(m, k);
509
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
510
+ desc = { enumerable: true, get: function() {
511
+ return m[k];
512
+ } };
513
+ }
514
+ Object.defineProperty(o, k2, desc);
515
+ }) : (function(o, m, k, k2) {
516
+ if (k2 === void 0) k2 = k;
517
+ o[k2] = m[k];
518
+ }));
519
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
520
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
521
+ }) : function(o, v) {
522
+ o["default"] = v;
523
+ });
524
+ var __importStar = exports && exports.__importStar || function(mod) {
525
+ if (mod && mod.__esModule) return mod;
526
+ var result = {};
527
+ if (mod != null) {
528
+ for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
529
+ }
530
+ __setModuleDefault(result, mod);
531
+ return result;
532
+ };
533
+ Object.defineProperty(exports, "__esModule", { value: true });
534
+ exports.ADDRESS_BOUNDARY = void 0;
535
+ exports.groupPossibilities = groupPossibilities;
536
+ exports.padGroup = padGroup;
537
+ exports.simpleRegularExpression = simpleRegularExpression;
538
+ exports.possibleElisions = possibleElisions;
539
+ var v6 = __importStar(require_constants2());
540
+ function groupPossibilities(possibilities) {
541
+ return `(${possibilities.join("|")})`;
542
+ }
543
+ function padGroup(group) {
544
+ if (group.length < 4) {
545
+ return `0{0,${4 - group.length}}${group}`;
546
+ }
547
+ return group;
548
+ }
549
+ exports.ADDRESS_BOUNDARY = "[^A-Fa-f0-9:]";
550
+ function simpleRegularExpression(groups) {
551
+ const zeroIndexes = [];
552
+ groups.forEach((group, i) => {
553
+ const groupInteger = parseInt(group, 16);
554
+ if (groupInteger === 0) {
555
+ zeroIndexes.push(i);
556
+ }
557
+ });
558
+ const possibilities = zeroIndexes.map((zeroIndex) => groups.map((group, i) => {
559
+ if (i === zeroIndex) {
560
+ const elision = i === 0 || i === v6.GROUPS - 1 ? ":" : "";
561
+ return groupPossibilities([padGroup(group), elision]);
562
+ }
563
+ return padGroup(group);
564
+ }).join(":"));
565
+ possibilities.push(groups.map(padGroup).join(":"));
566
+ return groupPossibilities(possibilities);
567
+ }
568
+ function possibleElisions(elidedGroups, moreLeft, moreRight) {
569
+ const left = moreLeft ? "" : ":";
570
+ const right = moreRight ? "" : ":";
571
+ const possibilities = [];
572
+ if (!moreLeft && !moreRight) {
573
+ possibilities.push("::");
574
+ }
575
+ if (moreLeft && moreRight) {
576
+ possibilities.push("");
577
+ }
578
+ if (moreRight && !moreLeft || !moreRight && moreLeft) {
579
+ possibilities.push(":");
580
+ }
581
+ possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`);
582
+ possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`);
583
+ possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`);
584
+ for (let groups = 1; groups < elidedGroups - 1; groups++) {
585
+ for (let position = 1; position < elidedGroups - groups; position++) {
586
+ possibilities.push(`(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`);
587
+ }
588
+ }
589
+ return groupPossibilities(possibilities);
590
+ }
218
591
  }
219
- sql += " ORDER BY created_at_epoch DESC LIMIT ?";
220
- params.push(limit);
221
- const stmt = db2.query(sql);
222
- return stmt.all(...params);
223
- }
224
- function getObservationsByIds(db2, ids) {
225
- if (ids.length === 0) return [];
226
- const placeholders = ids.map(() => "?").join(",");
227
- const sql = `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`;
228
- const stmt = db2.query(sql);
229
- return stmt.all(...ids);
230
- }
231
- function getTimeline(db2, anchorId, depthBefore = 5, depthAfter = 5) {
232
- const anchorStmt = db2.query("SELECT created_at_epoch FROM observations WHERE id = ?");
233
- const anchor = anchorStmt.get(anchorId);
234
- if (!anchor) return [];
235
- const anchorEpoch = anchor.created_at_epoch;
236
- const beforeStmt = db2.query(`
237
- SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
238
- FROM observations
239
- WHERE created_at_epoch < ?
240
- ORDER BY created_at_epoch DESC
241
- LIMIT ?
242
- `);
243
- const before = beforeStmt.all(anchorEpoch, depthBefore).reverse();
244
- const selfStmt = db2.query(`
245
- SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
246
- FROM observations WHERE id = ?
247
- `);
248
- const self = selfStmt.all(anchorId);
249
- const afterStmt = db2.query(`
250
- SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
251
- FROM observations
252
- WHERE created_at_epoch > ?
253
- ORDER BY created_at_epoch ASC
254
- LIMIT ?
255
- `);
256
- const after = afterStmt.all(anchorEpoch, depthAfter);
257
- return [...before, ...self, ...after];
258
- }
259
- function getProjectStats(db2, project) {
260
- const obsStmt = db2.query("SELECT COUNT(*) as count FROM observations WHERE project = ?");
261
- const sumStmt = db2.query("SELECT COUNT(*) as count FROM summaries WHERE project = ?");
262
- const sesStmt = db2.query("SELECT COUNT(*) as count FROM sessions WHERE project = ?");
263
- const prmStmt = db2.query("SELECT COUNT(*) as count FROM prompts WHERE project = ?");
264
- return {
265
- observations: obsStmt.get(project)?.count || 0,
266
- summaries: sumStmt.get(project)?.count || 0,
267
- sessions: sesStmt.get(project)?.count || 0,
268
- prompts: prmStmt.get(project)?.count || 0
592
+ });
593
+
594
+ // node_modules/ip-address/dist/ipv6.js
595
+ var require_ipv6 = __commonJS({
596
+ "node_modules/ip-address/dist/ipv6.js"(exports) {
597
+ "use strict";
598
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
599
+ if (k2 === void 0) k2 = k;
600
+ var desc = Object.getOwnPropertyDescriptor(m, k);
601
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
602
+ desc = { enumerable: true, get: function() {
603
+ return m[k];
604
+ } };
605
+ }
606
+ Object.defineProperty(o, k2, desc);
607
+ }) : (function(o, m, k, k2) {
608
+ if (k2 === void 0) k2 = k;
609
+ o[k2] = m[k];
610
+ }));
611
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
612
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
613
+ }) : function(o, v) {
614
+ o["default"] = v;
615
+ });
616
+ var __importStar = exports && exports.__importStar || function(mod) {
617
+ if (mod && mod.__esModule) return mod;
618
+ var result = {};
619
+ if (mod != null) {
620
+ for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
621
+ }
622
+ __setModuleDefault(result, mod);
623
+ return result;
624
+ };
625
+ Object.defineProperty(exports, "__esModule", { value: true });
626
+ exports.Address6 = void 0;
627
+ var common = __importStar(require_common());
628
+ var constants4 = __importStar(require_constants());
629
+ var constants6 = __importStar(require_constants2());
630
+ var helpers = __importStar(require_helpers());
631
+ var ipv4_1 = require_ipv4();
632
+ var regular_expressions_1 = require_regular_expressions();
633
+ var address_error_1 = require_address_error();
634
+ var common_1 = require_common();
635
+ function assert(condition) {
636
+ if (!condition) {
637
+ throw new Error("Assertion failed.");
638
+ }
639
+ }
640
+ function addCommas(number) {
641
+ const r = /(\d+)(\d{3})/;
642
+ while (r.test(number)) {
643
+ number = number.replace(r, "$1,$2");
644
+ }
645
+ return number;
646
+ }
647
+ function spanLeadingZeroes4(n) {
648
+ n = n.replace(/^(0{1,})([1-9]+)$/, '<span class="parse-error">$1</span>$2');
649
+ n = n.replace(/^(0{1,})(0)$/, '<span class="parse-error">$1</span>$2');
650
+ return n;
651
+ }
652
+ function compact(address, slice) {
653
+ const s1 = [];
654
+ const s2 = [];
655
+ let i;
656
+ for (i = 0; i < address.length; i++) {
657
+ if (i < slice[0]) {
658
+ s1.push(address[i]);
659
+ } else if (i > slice[1]) {
660
+ s2.push(address[i]);
661
+ }
662
+ }
663
+ return s1.concat(["compact"]).concat(s2);
664
+ }
665
+ function paddedHex(octet) {
666
+ return parseInt(octet, 16).toString(16).padStart(4, "0");
667
+ }
668
+ function unsignByte(b) {
669
+ return b & 255;
670
+ }
671
+ var Address62 = class _Address6 {
672
+ constructor(address, optionalGroups) {
673
+ this.addressMinusSuffix = "";
674
+ this.parsedSubnet = "";
675
+ this.subnet = "/128";
676
+ this.subnetMask = 128;
677
+ this.v4 = false;
678
+ this.zone = "";
679
+ this.isInSubnet = common.isInSubnet;
680
+ this.isCorrect = common.isCorrect(constants6.BITS);
681
+ if (optionalGroups === void 0) {
682
+ this.groups = constants6.GROUPS;
683
+ } else {
684
+ this.groups = optionalGroups;
685
+ }
686
+ this.address = address;
687
+ const subnet = constants6.RE_SUBNET_STRING.exec(address);
688
+ if (subnet) {
689
+ this.parsedSubnet = subnet[0].replace("/", "");
690
+ this.subnetMask = parseInt(this.parsedSubnet, 10);
691
+ this.subnet = `/${this.subnetMask}`;
692
+ if (Number.isNaN(this.subnetMask) || this.subnetMask < 0 || this.subnetMask > constants6.BITS) {
693
+ throw new address_error_1.AddressError("Invalid subnet mask.");
694
+ }
695
+ address = address.replace(constants6.RE_SUBNET_STRING, "");
696
+ } else if (/\//.test(address)) {
697
+ throw new address_error_1.AddressError("Invalid subnet mask.");
698
+ }
699
+ const zone = constants6.RE_ZONE_STRING.exec(address);
700
+ if (zone) {
701
+ this.zone = zone[0];
702
+ address = address.replace(constants6.RE_ZONE_STRING, "");
703
+ }
704
+ this.addressMinusSuffix = address;
705
+ this.parsedAddress = this.parse(this.addressMinusSuffix);
706
+ }
707
+ static isValid(address) {
708
+ try {
709
+ new _Address6(address);
710
+ return true;
711
+ } catch (e) {
712
+ return false;
713
+ }
714
+ }
715
+ /**
716
+ * Convert a BigInt to a v6 address object
717
+ * @memberof Address6
718
+ * @static
719
+ * @param {bigint} bigInt - a BigInt to convert
720
+ * @returns {Address6}
721
+ * @example
722
+ * var bigInt = BigInt('1000000000000');
723
+ * var address = Address6.fromBigInt(bigInt);
724
+ * address.correctForm(); // '::e8:d4a5:1000'
725
+ */
726
+ static fromBigInt(bigInt) {
727
+ const hex = bigInt.toString(16).padStart(32, "0");
728
+ const groups = [];
729
+ let i;
730
+ for (i = 0; i < constants6.GROUPS; i++) {
731
+ groups.push(hex.slice(i * 4, (i + 1) * 4));
732
+ }
733
+ return new _Address6(groups.join(":"));
734
+ }
735
+ /**
736
+ * Convert a URL (with optional port number) to an address object
737
+ * @memberof Address6
738
+ * @static
739
+ * @param {string} url - a URL with optional port number
740
+ * @example
741
+ * var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');
742
+ * addressAndPort.address.correctForm(); // 'ffff::'
743
+ * addressAndPort.port; // 8080
744
+ */
745
+ static fromURL(url) {
746
+ let host;
747
+ let port = null;
748
+ let result;
749
+ if (url.indexOf("[") !== -1 && url.indexOf("]:") !== -1) {
750
+ result = constants6.RE_URL_WITH_PORT.exec(url);
751
+ if (result === null) {
752
+ return {
753
+ error: "failed to parse address with port",
754
+ address: null,
755
+ port: null
756
+ };
757
+ }
758
+ host = result[1];
759
+ port = result[2];
760
+ } else if (url.indexOf("/") !== -1) {
761
+ url = url.replace(/^[a-z0-9]+:\/\//, "");
762
+ result = constants6.RE_URL.exec(url);
763
+ if (result === null) {
764
+ return {
765
+ error: "failed to parse address from URL",
766
+ address: null,
767
+ port: null
768
+ };
769
+ }
770
+ host = result[1];
771
+ } else {
772
+ host = url;
773
+ }
774
+ if (port) {
775
+ port = parseInt(port, 10);
776
+ if (port < 0 || port > 65536) {
777
+ port = null;
778
+ }
779
+ } else {
780
+ port = null;
781
+ }
782
+ return {
783
+ address: new _Address6(host),
784
+ port
785
+ };
786
+ }
787
+ /**
788
+ * Create an IPv6-mapped address given an IPv4 address
789
+ * @memberof Address6
790
+ * @static
791
+ * @param {string} address - An IPv4 address string
792
+ * @returns {Address6}
793
+ * @example
794
+ * var address = Address6.fromAddress4('192.168.0.1');
795
+ * address.correctForm(); // '::ffff:c0a8:1'
796
+ * address.to4in6(); // '::ffff:192.168.0.1'
797
+ */
798
+ static fromAddress4(address) {
799
+ const address4 = new ipv4_1.Address4(address);
800
+ const mask6 = constants6.BITS - (constants4.BITS - address4.subnetMask);
801
+ return new _Address6(`::ffff:${address4.correctForm()}/${mask6}`);
802
+ }
803
+ /**
804
+ * Return an address from ip6.arpa form
805
+ * @memberof Address6
806
+ * @static
807
+ * @param {string} arpaFormAddress - an 'ip6.arpa' form address
808
+ * @returns {Adress6}
809
+ * @example
810
+ * var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)
811
+ * address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'
812
+ */
813
+ static fromArpa(arpaFormAddress) {
814
+ let address = arpaFormAddress.replace(/(\.ip6\.arpa)?\.$/, "");
815
+ const semicolonAmount = 7;
816
+ if (address.length !== 63) {
817
+ throw new address_error_1.AddressError("Invalid 'ip6.arpa' form.");
818
+ }
819
+ const parts = address.split(".").reverse();
820
+ for (let i = semicolonAmount; i > 0; i--) {
821
+ const insertIndex = i * 4;
822
+ parts.splice(insertIndex, 0, ":");
823
+ }
824
+ address = parts.join("");
825
+ return new _Address6(address);
826
+ }
827
+ /**
828
+ * Return the Microsoft UNC transcription of the address
829
+ * @memberof Address6
830
+ * @instance
831
+ * @returns {String} the Microsoft UNC transcription of the address
832
+ */
833
+ microsoftTranscription() {
834
+ return `${this.correctForm().replace(/:/g, "-")}.ipv6-literal.net`;
835
+ }
836
+ /**
837
+ * Return the first n bits of the address, defaulting to the subnet mask
838
+ * @memberof Address6
839
+ * @instance
840
+ * @param {number} [mask=subnet] - the number of bits to mask
841
+ * @returns {String} the first n bits of the address as a string
842
+ */
843
+ mask(mask = this.subnetMask) {
844
+ return this.getBitsBase2(0, mask);
845
+ }
846
+ /**
847
+ * Return the number of possible subnets of a given size in the address
848
+ * @memberof Address6
849
+ * @instance
850
+ * @param {number} [subnetSize=128] - the subnet size
851
+ * @returns {String}
852
+ */
853
+ // TODO: probably useful to have a numeric version of this too
854
+ possibleSubnets(subnetSize = 128) {
855
+ const availableBits = constants6.BITS - this.subnetMask;
856
+ const subnetBits = Math.abs(subnetSize - constants6.BITS);
857
+ const subnetPowers = availableBits - subnetBits;
858
+ if (subnetPowers < 0) {
859
+ return "0";
860
+ }
861
+ return addCommas((BigInt("2") ** BigInt(subnetPowers)).toString(10));
862
+ }
863
+ /**
864
+ * Helper function getting start address.
865
+ * @memberof Address6
866
+ * @instance
867
+ * @returns {bigint}
868
+ */
869
+ _startAddress() {
870
+ return BigInt(`0b${this.mask() + "0".repeat(constants6.BITS - this.subnetMask)}`);
871
+ }
872
+ /**
873
+ * The first address in the range given by this address' subnet
874
+ * Often referred to as the Network Address.
875
+ * @memberof Address6
876
+ * @instance
877
+ * @returns {Address6}
878
+ */
879
+ startAddress() {
880
+ return _Address6.fromBigInt(this._startAddress());
881
+ }
882
+ /**
883
+ * The first host address in the range given by this address's subnet ie
884
+ * the first address after the Network Address
885
+ * @memberof Address6
886
+ * @instance
887
+ * @returns {Address6}
888
+ */
889
+ startAddressExclusive() {
890
+ const adjust = BigInt("1");
891
+ return _Address6.fromBigInt(this._startAddress() + adjust);
892
+ }
893
+ /**
894
+ * Helper function getting end address.
895
+ * @memberof Address6
896
+ * @instance
897
+ * @returns {bigint}
898
+ */
899
+ _endAddress() {
900
+ return BigInt(`0b${this.mask() + "1".repeat(constants6.BITS - this.subnetMask)}`);
901
+ }
902
+ /**
903
+ * The last address in the range given by this address' subnet
904
+ * Often referred to as the Broadcast
905
+ * @memberof Address6
906
+ * @instance
907
+ * @returns {Address6}
908
+ */
909
+ endAddress() {
910
+ return _Address6.fromBigInt(this._endAddress());
911
+ }
912
+ /**
913
+ * The last host address in the range given by this address's subnet ie
914
+ * the last address prior to the Broadcast Address
915
+ * @memberof Address6
916
+ * @instance
917
+ * @returns {Address6}
918
+ */
919
+ endAddressExclusive() {
920
+ const adjust = BigInt("1");
921
+ return _Address6.fromBigInt(this._endAddress() - adjust);
922
+ }
923
+ /**
924
+ * Return the scope of the address
925
+ * @memberof Address6
926
+ * @instance
927
+ * @returns {String}
928
+ */
929
+ getScope() {
930
+ let scope = constants6.SCOPES[parseInt(this.getBits(12, 16).toString(10), 10)];
931
+ if (this.getType() === "Global unicast" && scope !== "Link local") {
932
+ scope = "Global";
933
+ }
934
+ return scope || "Unknown";
935
+ }
936
+ /**
937
+ * Return the type of the address
938
+ * @memberof Address6
939
+ * @instance
940
+ * @returns {String}
941
+ */
942
+ getType() {
943
+ for (const subnet of Object.keys(constants6.TYPES)) {
944
+ if (this.isInSubnet(new _Address6(subnet))) {
945
+ return constants6.TYPES[subnet];
946
+ }
947
+ }
948
+ return "Global unicast";
949
+ }
950
+ /**
951
+ * Return the bits in the given range as a BigInt
952
+ * @memberof Address6
953
+ * @instance
954
+ * @returns {bigint}
955
+ */
956
+ getBits(start, end) {
957
+ return BigInt(`0b${this.getBitsBase2(start, end)}`);
958
+ }
959
+ /**
960
+ * Return the bits in the given range as a base-2 string
961
+ * @memberof Address6
962
+ * @instance
963
+ * @returns {String}
964
+ */
965
+ getBitsBase2(start, end) {
966
+ return this.binaryZeroPad().slice(start, end);
967
+ }
968
+ /**
969
+ * Return the bits in the given range as a base-16 string
970
+ * @memberof Address6
971
+ * @instance
972
+ * @returns {String}
973
+ */
974
+ getBitsBase16(start, end) {
975
+ const length = end - start;
976
+ if (length % 4 !== 0) {
977
+ throw new Error("Length of bits to retrieve must be divisible by four");
978
+ }
979
+ return this.getBits(start, end).toString(16).padStart(length / 4, "0");
980
+ }
981
+ /**
982
+ * Return the bits that are set past the subnet mask length
983
+ * @memberof Address6
984
+ * @instance
985
+ * @returns {String}
986
+ */
987
+ getBitsPastSubnet() {
988
+ return this.getBitsBase2(this.subnetMask, constants6.BITS);
989
+ }
990
+ /**
991
+ * Return the reversed ip6.arpa form of the address
992
+ * @memberof Address6
993
+ * @param {Object} options
994
+ * @param {boolean} options.omitSuffix - omit the "ip6.arpa" suffix
995
+ * @instance
996
+ * @returns {String}
997
+ */
998
+ reverseForm(options) {
999
+ if (!options) {
1000
+ options = {};
1001
+ }
1002
+ const characters = Math.floor(this.subnetMask / 4);
1003
+ const reversed = this.canonicalForm().replace(/:/g, "").split("").slice(0, characters).reverse().join(".");
1004
+ if (characters > 0) {
1005
+ if (options.omitSuffix) {
1006
+ return reversed;
1007
+ }
1008
+ return `${reversed}.ip6.arpa.`;
1009
+ }
1010
+ if (options.omitSuffix) {
1011
+ return "";
1012
+ }
1013
+ return "ip6.arpa.";
1014
+ }
1015
+ /**
1016
+ * Return the correct form of the address
1017
+ * @memberof Address6
1018
+ * @instance
1019
+ * @returns {String}
1020
+ */
1021
+ correctForm() {
1022
+ let i;
1023
+ let groups = [];
1024
+ let zeroCounter = 0;
1025
+ const zeroes = [];
1026
+ for (i = 0; i < this.parsedAddress.length; i++) {
1027
+ const value = parseInt(this.parsedAddress[i], 16);
1028
+ if (value === 0) {
1029
+ zeroCounter++;
1030
+ }
1031
+ if (value !== 0 && zeroCounter > 0) {
1032
+ if (zeroCounter > 1) {
1033
+ zeroes.push([i - zeroCounter, i - 1]);
1034
+ }
1035
+ zeroCounter = 0;
1036
+ }
1037
+ }
1038
+ if (zeroCounter > 1) {
1039
+ zeroes.push([this.parsedAddress.length - zeroCounter, this.parsedAddress.length - 1]);
1040
+ }
1041
+ const zeroLengths = zeroes.map((n) => n[1] - n[0] + 1);
1042
+ if (zeroes.length > 0) {
1043
+ const index = zeroLengths.indexOf(Math.max(...zeroLengths));
1044
+ groups = compact(this.parsedAddress, zeroes[index]);
1045
+ } else {
1046
+ groups = this.parsedAddress;
1047
+ }
1048
+ for (i = 0; i < groups.length; i++) {
1049
+ if (groups[i] !== "compact") {
1050
+ groups[i] = parseInt(groups[i], 16).toString(16);
1051
+ }
1052
+ }
1053
+ let correct = groups.join(":");
1054
+ correct = correct.replace(/^compact$/, "::");
1055
+ correct = correct.replace(/(^compact)|(compact$)/, ":");
1056
+ correct = correct.replace(/compact/, "");
1057
+ return correct;
1058
+ }
1059
+ /**
1060
+ * Return a zero-padded base-2 string representation of the address
1061
+ * @memberof Address6
1062
+ * @instance
1063
+ * @returns {String}
1064
+ * @example
1065
+ * var address = new Address6('2001:4860:4001:803::1011');
1066
+ * address.binaryZeroPad();
1067
+ * // '0010000000000001010010000110000001000000000000010000100000000011
1068
+ * // 0000000000000000000000000000000000000000000000000001000000010001'
1069
+ */
1070
+ binaryZeroPad() {
1071
+ return this.bigInt().toString(2).padStart(constants6.BITS, "0");
1072
+ }
1073
+ // TODO: Improve the semantics of this helper function
1074
+ parse4in6(address) {
1075
+ const groups = address.split(":");
1076
+ const lastGroup = groups.slice(-1)[0];
1077
+ const address4 = lastGroup.match(constants4.RE_ADDRESS);
1078
+ if (address4) {
1079
+ this.parsedAddress4 = address4[0];
1080
+ this.address4 = new ipv4_1.Address4(this.parsedAddress4);
1081
+ for (let i = 0; i < this.address4.groups; i++) {
1082
+ if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) {
1083
+ throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.", address.replace(constants4.RE_ADDRESS, this.address4.parsedAddress.map(spanLeadingZeroes4).join(".")));
1084
+ }
1085
+ }
1086
+ this.v4 = true;
1087
+ groups[groups.length - 1] = this.address4.toGroup6();
1088
+ address = groups.join(":");
1089
+ }
1090
+ return address;
1091
+ }
1092
+ // TODO: Make private?
1093
+ parse(address) {
1094
+ address = this.parse4in6(address);
1095
+ const badCharacters = address.match(constants6.RE_BAD_CHARACTERS);
1096
+ if (badCharacters) {
1097
+ throw new address_error_1.AddressError(`Bad character${badCharacters.length > 1 ? "s" : ""} detected in address: ${badCharacters.join("")}`, address.replace(constants6.RE_BAD_CHARACTERS, '<span class="parse-error">$1</span>'));
1098
+ }
1099
+ const badAddress = address.match(constants6.RE_BAD_ADDRESS);
1100
+ if (badAddress) {
1101
+ throw new address_error_1.AddressError(`Address failed regex: ${badAddress.join("")}`, address.replace(constants6.RE_BAD_ADDRESS, '<span class="parse-error">$1</span>'));
1102
+ }
1103
+ let groups = [];
1104
+ const halves = address.split("::");
1105
+ if (halves.length === 2) {
1106
+ let first = halves[0].split(":");
1107
+ let last = halves[1].split(":");
1108
+ if (first.length === 1 && first[0] === "") {
1109
+ first = [];
1110
+ }
1111
+ if (last.length === 1 && last[0] === "") {
1112
+ last = [];
1113
+ }
1114
+ const remaining = this.groups - (first.length + last.length);
1115
+ if (!remaining) {
1116
+ throw new address_error_1.AddressError("Error parsing groups");
1117
+ }
1118
+ this.elidedGroups = remaining;
1119
+ this.elisionBegin = first.length;
1120
+ this.elisionEnd = first.length + this.elidedGroups;
1121
+ groups = groups.concat(first);
1122
+ for (let i = 0; i < remaining; i++) {
1123
+ groups.push("0");
1124
+ }
1125
+ groups = groups.concat(last);
1126
+ } else if (halves.length === 1) {
1127
+ groups = address.split(":");
1128
+ this.elidedGroups = 0;
1129
+ } else {
1130
+ throw new address_error_1.AddressError("Too many :: groups found");
1131
+ }
1132
+ groups = groups.map((group) => parseInt(group, 16).toString(16));
1133
+ if (groups.length !== this.groups) {
1134
+ throw new address_error_1.AddressError("Incorrect number of groups found");
1135
+ }
1136
+ return groups;
1137
+ }
1138
+ /**
1139
+ * Return the canonical form of the address
1140
+ * @memberof Address6
1141
+ * @instance
1142
+ * @returns {String}
1143
+ */
1144
+ canonicalForm() {
1145
+ return this.parsedAddress.map(paddedHex).join(":");
1146
+ }
1147
+ /**
1148
+ * Return the decimal form of the address
1149
+ * @memberof Address6
1150
+ * @instance
1151
+ * @returns {String}
1152
+ */
1153
+ decimal() {
1154
+ return this.parsedAddress.map((n) => parseInt(n, 16).toString(10).padStart(5, "0")).join(":");
1155
+ }
1156
+ /**
1157
+ * Return the address as a BigInt
1158
+ * @memberof Address6
1159
+ * @instance
1160
+ * @returns {bigint}
1161
+ */
1162
+ bigInt() {
1163
+ return BigInt(`0x${this.parsedAddress.map(paddedHex).join("")}`);
1164
+ }
1165
+ /**
1166
+ * Return the last two groups of this address as an IPv4 address string
1167
+ * @memberof Address6
1168
+ * @instance
1169
+ * @returns {Address4}
1170
+ * @example
1171
+ * var address = new Address6('2001:4860:4001::1825:bf11');
1172
+ * address.to4().correctForm(); // '24.37.191.17'
1173
+ */
1174
+ to4() {
1175
+ const binary = this.binaryZeroPad().split("");
1176
+ return ipv4_1.Address4.fromHex(BigInt(`0b${binary.slice(96, 128).join("")}`).toString(16));
1177
+ }
1178
+ /**
1179
+ * Return the v4-in-v6 form of the address
1180
+ * @memberof Address6
1181
+ * @instance
1182
+ * @returns {String}
1183
+ */
1184
+ to4in6() {
1185
+ const address4 = this.to4();
1186
+ const address6 = new _Address6(this.parsedAddress.slice(0, 6).join(":"), 6);
1187
+ const correct = address6.correctForm();
1188
+ let infix = "";
1189
+ if (!/:$/.test(correct)) {
1190
+ infix = ":";
1191
+ }
1192
+ return correct + infix + address4.address;
1193
+ }
1194
+ /**
1195
+ * Return an object containing the Teredo properties of the address
1196
+ * @memberof Address6
1197
+ * @instance
1198
+ * @returns {Object}
1199
+ */
1200
+ inspectTeredo() {
1201
+ const prefix = this.getBitsBase16(0, 32);
1202
+ const bitsForUdpPort = this.getBits(80, 96);
1203
+ const udpPort = (bitsForUdpPort ^ BigInt("0xffff")).toString();
1204
+ const server4 = ipv4_1.Address4.fromHex(this.getBitsBase16(32, 64));
1205
+ const bitsForClient4 = this.getBits(96, 128);
1206
+ const client4 = ipv4_1.Address4.fromHex((bitsForClient4 ^ BigInt("0xffffffff")).toString(16));
1207
+ const flagsBase2 = this.getBitsBase2(64, 80);
1208
+ const coneNat = (0, common_1.testBit)(flagsBase2, 15);
1209
+ const reserved = (0, common_1.testBit)(flagsBase2, 14);
1210
+ const groupIndividual = (0, common_1.testBit)(flagsBase2, 8);
1211
+ const universalLocal = (0, common_1.testBit)(flagsBase2, 9);
1212
+ const nonce = BigInt(`0b${flagsBase2.slice(2, 6) + flagsBase2.slice(8, 16)}`).toString(10);
1213
+ return {
1214
+ prefix: `${prefix.slice(0, 4)}:${prefix.slice(4, 8)}`,
1215
+ server4: server4.address,
1216
+ client4: client4.address,
1217
+ flags: flagsBase2,
1218
+ coneNat,
1219
+ microsoft: {
1220
+ reserved,
1221
+ universalLocal,
1222
+ groupIndividual,
1223
+ nonce
1224
+ },
1225
+ udpPort
1226
+ };
1227
+ }
1228
+ /**
1229
+ * Return an object containing the 6to4 properties of the address
1230
+ * @memberof Address6
1231
+ * @instance
1232
+ * @returns {Object}
1233
+ */
1234
+ inspect6to4() {
1235
+ const prefix = this.getBitsBase16(0, 16);
1236
+ const gateway = ipv4_1.Address4.fromHex(this.getBitsBase16(16, 48));
1237
+ return {
1238
+ prefix: prefix.slice(0, 4),
1239
+ gateway: gateway.address
1240
+ };
1241
+ }
1242
+ /**
1243
+ * Return a v6 6to4 address from a v6 v4inv6 address
1244
+ * @memberof Address6
1245
+ * @instance
1246
+ * @returns {Address6}
1247
+ */
1248
+ to6to4() {
1249
+ if (!this.is4()) {
1250
+ return null;
1251
+ }
1252
+ const addr6to4 = [
1253
+ "2002",
1254
+ this.getBitsBase16(96, 112),
1255
+ this.getBitsBase16(112, 128),
1256
+ "",
1257
+ "/16"
1258
+ ].join(":");
1259
+ return new _Address6(addr6to4);
1260
+ }
1261
+ /**
1262
+ * Return a byte array
1263
+ * @memberof Address6
1264
+ * @instance
1265
+ * @returns {Array}
1266
+ */
1267
+ toByteArray() {
1268
+ const valueWithoutPadding = this.bigInt().toString(16);
1269
+ const leadingPad = "0".repeat(valueWithoutPadding.length % 2);
1270
+ const value = `${leadingPad}${valueWithoutPadding}`;
1271
+ const bytes = [];
1272
+ for (let i = 0, length = value.length; i < length; i += 2) {
1273
+ bytes.push(parseInt(value.substring(i, i + 2), 16));
1274
+ }
1275
+ return bytes;
1276
+ }
1277
+ /**
1278
+ * Return an unsigned byte array
1279
+ * @memberof Address6
1280
+ * @instance
1281
+ * @returns {Array}
1282
+ */
1283
+ toUnsignedByteArray() {
1284
+ return this.toByteArray().map(unsignByte);
1285
+ }
1286
+ /**
1287
+ * Convert a byte array to an Address6 object
1288
+ * @memberof Address6
1289
+ * @static
1290
+ * @returns {Address6}
1291
+ */
1292
+ static fromByteArray(bytes) {
1293
+ return this.fromUnsignedByteArray(bytes.map(unsignByte));
1294
+ }
1295
+ /**
1296
+ * Convert an unsigned byte array to an Address6 object
1297
+ * @memberof Address6
1298
+ * @static
1299
+ * @returns {Address6}
1300
+ */
1301
+ static fromUnsignedByteArray(bytes) {
1302
+ const BYTE_MAX = BigInt("256");
1303
+ let result = BigInt("0");
1304
+ let multiplier = BigInt("1");
1305
+ for (let i = bytes.length - 1; i >= 0; i--) {
1306
+ result += multiplier * BigInt(bytes[i].toString(10));
1307
+ multiplier *= BYTE_MAX;
1308
+ }
1309
+ return _Address6.fromBigInt(result);
1310
+ }
1311
+ /**
1312
+ * Returns true if the address is in the canonical form, false otherwise
1313
+ * @memberof Address6
1314
+ * @instance
1315
+ * @returns {boolean}
1316
+ */
1317
+ isCanonical() {
1318
+ return this.addressMinusSuffix === this.canonicalForm();
1319
+ }
1320
+ /**
1321
+ * Returns true if the address is a link local address, false otherwise
1322
+ * @memberof Address6
1323
+ * @instance
1324
+ * @returns {boolean}
1325
+ */
1326
+ isLinkLocal() {
1327
+ if (this.getBitsBase2(0, 64) === "1111111010000000000000000000000000000000000000000000000000000000") {
1328
+ return true;
1329
+ }
1330
+ return false;
1331
+ }
1332
+ /**
1333
+ * Returns true if the address is a multicast address, false otherwise
1334
+ * @memberof Address6
1335
+ * @instance
1336
+ * @returns {boolean}
1337
+ */
1338
+ isMulticast() {
1339
+ return this.getType() === "Multicast";
1340
+ }
1341
+ /**
1342
+ * Returns true if the address is a v4-in-v6 address, false otherwise
1343
+ * @memberof Address6
1344
+ * @instance
1345
+ * @returns {boolean}
1346
+ */
1347
+ is4() {
1348
+ return this.v4;
1349
+ }
1350
+ /**
1351
+ * Returns true if the address is a Teredo address, false otherwise
1352
+ * @memberof Address6
1353
+ * @instance
1354
+ * @returns {boolean}
1355
+ */
1356
+ isTeredo() {
1357
+ return this.isInSubnet(new _Address6("2001::/32"));
1358
+ }
1359
+ /**
1360
+ * Returns true if the address is a 6to4 address, false otherwise
1361
+ * @memberof Address6
1362
+ * @instance
1363
+ * @returns {boolean}
1364
+ */
1365
+ is6to4() {
1366
+ return this.isInSubnet(new _Address6("2002::/16"));
1367
+ }
1368
+ /**
1369
+ * Returns true if the address is a loopback address, false otherwise
1370
+ * @memberof Address6
1371
+ * @instance
1372
+ * @returns {boolean}
1373
+ */
1374
+ isLoopback() {
1375
+ return this.getType() === "Loopback";
1376
+ }
1377
+ // #endregion
1378
+ // #region HTML
1379
+ /**
1380
+ * @returns {String} the address in link form with a default port of 80
1381
+ */
1382
+ href(optionalPort) {
1383
+ if (optionalPort === void 0) {
1384
+ optionalPort = "";
1385
+ } else {
1386
+ optionalPort = `:${optionalPort}`;
1387
+ }
1388
+ return `http://[${this.correctForm()}]${optionalPort}/`;
1389
+ }
1390
+ /**
1391
+ * @returns {String} a link suitable for conveying the address via a URL hash
1392
+ */
1393
+ link(options) {
1394
+ if (!options) {
1395
+ options = {};
1396
+ }
1397
+ if (options.className === void 0) {
1398
+ options.className = "";
1399
+ }
1400
+ if (options.prefix === void 0) {
1401
+ options.prefix = "/#address=";
1402
+ }
1403
+ if (options.v4 === void 0) {
1404
+ options.v4 = false;
1405
+ }
1406
+ let formFunction = this.correctForm;
1407
+ if (options.v4) {
1408
+ formFunction = this.to4in6;
1409
+ }
1410
+ const form = formFunction.call(this);
1411
+ if (options.className) {
1412
+ return `<a href="${options.prefix}${form}" class="${options.className}">${form}</a>`;
1413
+ }
1414
+ return `<a href="${options.prefix}${form}">${form}</a>`;
1415
+ }
1416
+ /**
1417
+ * Groups an address
1418
+ * @returns {String}
1419
+ */
1420
+ group() {
1421
+ if (this.elidedGroups === 0) {
1422
+ return helpers.simpleGroup(this.address).join(":");
1423
+ }
1424
+ assert(typeof this.elidedGroups === "number");
1425
+ assert(typeof this.elisionBegin === "number");
1426
+ const output = [];
1427
+ const [left, right] = this.address.split("::");
1428
+ if (left.length) {
1429
+ output.push(...helpers.simpleGroup(left));
1430
+ } else {
1431
+ output.push("");
1432
+ }
1433
+ const classes = ["hover-group"];
1434
+ for (let i = this.elisionBegin; i < this.elisionBegin + this.elidedGroups; i++) {
1435
+ classes.push(`group-${i}`);
1436
+ }
1437
+ output.push(`<span class="${classes.join(" ")}"></span>`);
1438
+ if (right.length) {
1439
+ output.push(...helpers.simpleGroup(right, this.elisionEnd));
1440
+ } else {
1441
+ output.push("");
1442
+ }
1443
+ if (this.is4()) {
1444
+ assert(this.address4 instanceof ipv4_1.Address4);
1445
+ output.pop();
1446
+ output.push(this.address4.groupForV6());
1447
+ }
1448
+ return output.join(":");
1449
+ }
1450
+ // #endregion
1451
+ // #region Regular expressions
1452
+ /**
1453
+ * Generate a regular expression string that can be used to find or validate
1454
+ * all variations of this address
1455
+ * @memberof Address6
1456
+ * @instance
1457
+ * @param {boolean} substringSearch
1458
+ * @returns {string}
1459
+ */
1460
+ regularExpressionString(substringSearch = false) {
1461
+ let output = [];
1462
+ const address6 = new _Address6(this.correctForm());
1463
+ if (address6.elidedGroups === 0) {
1464
+ output.push((0, regular_expressions_1.simpleRegularExpression)(address6.parsedAddress));
1465
+ } else if (address6.elidedGroups === constants6.GROUPS) {
1466
+ output.push((0, regular_expressions_1.possibleElisions)(constants6.GROUPS));
1467
+ } else {
1468
+ const halves = address6.address.split("::");
1469
+ if (halves[0].length) {
1470
+ output.push((0, regular_expressions_1.simpleRegularExpression)(halves[0].split(":")));
1471
+ }
1472
+ assert(typeof address6.elidedGroups === "number");
1473
+ output.push((0, regular_expressions_1.possibleElisions)(address6.elidedGroups, halves[0].length !== 0, halves[1].length !== 0));
1474
+ if (halves[1].length) {
1475
+ output.push((0, regular_expressions_1.simpleRegularExpression)(halves[1].split(":")));
1476
+ }
1477
+ output = [output.join(":")];
1478
+ }
1479
+ if (!substringSearch) {
1480
+ output = [
1481
+ "(?=^|",
1482
+ regular_expressions_1.ADDRESS_BOUNDARY,
1483
+ "|[^\\w\\:])(",
1484
+ ...output,
1485
+ ")(?=[^\\w\\:]|",
1486
+ regular_expressions_1.ADDRESS_BOUNDARY,
1487
+ "|$)"
1488
+ ];
1489
+ }
1490
+ return output.join("");
1491
+ }
1492
+ /**
1493
+ * Generate a regular expression that can be used to find or validate all
1494
+ * variations of this address.
1495
+ * @memberof Address6
1496
+ * @instance
1497
+ * @param {boolean} substringSearch
1498
+ * @returns {RegExp}
1499
+ */
1500
+ regularExpression(substringSearch = false) {
1501
+ return new RegExp(this.regularExpressionString(substringSearch), "i");
1502
+ }
1503
+ };
1504
+ exports.Address6 = Address62;
1505
+ }
1506
+ });
1507
+
1508
+ // node_modules/ip-address/dist/ip-address.js
1509
+ var require_ip_address = __commonJS({
1510
+ "node_modules/ip-address/dist/ip-address.js"(exports) {
1511
+ "use strict";
1512
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
1513
+ if (k2 === void 0) k2 = k;
1514
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1515
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1516
+ desc = { enumerable: true, get: function() {
1517
+ return m[k];
1518
+ } };
1519
+ }
1520
+ Object.defineProperty(o, k2, desc);
1521
+ }) : (function(o, m, k, k2) {
1522
+ if (k2 === void 0) k2 = k;
1523
+ o[k2] = m[k];
1524
+ }));
1525
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
1526
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1527
+ }) : function(o, v) {
1528
+ o["default"] = v;
1529
+ });
1530
+ var __importStar = exports && exports.__importStar || function(mod) {
1531
+ if (mod && mod.__esModule) return mod;
1532
+ var result = {};
1533
+ if (mod != null) {
1534
+ for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
1535
+ }
1536
+ __setModuleDefault(result, mod);
1537
+ return result;
1538
+ };
1539
+ Object.defineProperty(exports, "__esModule", { value: true });
1540
+ exports.v6 = exports.AddressError = exports.Address6 = exports.Address4 = void 0;
1541
+ var ipv4_1 = require_ipv4();
1542
+ Object.defineProperty(exports, "Address4", { enumerable: true, get: function() {
1543
+ return ipv4_1.Address4;
1544
+ } });
1545
+ var ipv6_1 = require_ipv6();
1546
+ Object.defineProperty(exports, "Address6", { enumerable: true, get: function() {
1547
+ return ipv6_1.Address6;
1548
+ } });
1549
+ var address_error_1 = require_address_error();
1550
+ Object.defineProperty(exports, "AddressError", { enumerable: true, get: function() {
1551
+ return address_error_1.AddressError;
1552
+ } });
1553
+ var helpers = __importStar(require_helpers());
1554
+ exports.v6 = { helpers };
1555
+ }
1556
+ });
1557
+
1558
+ // src/services/worker-service.ts
1559
+ import express from "express";
1560
+ import cors from "cors";
1561
+
1562
+ // node_modules/helmet/index.mjs
1563
+ var dangerouslyDisableDefaultSrc = /* @__PURE__ */ Symbol("dangerouslyDisableDefaultSrc");
1564
+ var SHOULD_BE_QUOTED = /* @__PURE__ */ new Set(["none", "self", "strict-dynamic", "report-sample", "inline-speculation-rules", "unsafe-inline", "unsafe-eval", "unsafe-hashes", "wasm-unsafe-eval"]);
1565
+ var getDefaultDirectives = () => ({
1566
+ "default-src": ["'self'"],
1567
+ "base-uri": ["'self'"],
1568
+ "font-src": ["'self'", "https:", "data:"],
1569
+ "form-action": ["'self'"],
1570
+ "frame-ancestors": ["'self'"],
1571
+ "img-src": ["'self'", "data:"],
1572
+ "object-src": ["'none'"],
1573
+ "script-src": ["'self'"],
1574
+ "script-src-attr": ["'none'"],
1575
+ "style-src": ["'self'", "https:", "'unsafe-inline'"],
1576
+ "upgrade-insecure-requests": []
1577
+ });
1578
+ var dashify = (str) => str.replace(/[A-Z]/g, (capitalLetter) => "-" + capitalLetter.toLowerCase());
1579
+ var assertDirectiveValueIsValid = (directiveName, directiveValue) => {
1580
+ if (/;|,/.test(directiveValue)) {
1581
+ throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`);
1582
+ }
1583
+ };
1584
+ var assertDirectiveValueEntryIsValid = (directiveName, directiveValueEntry) => {
1585
+ if (SHOULD_BE_QUOTED.has(directiveValueEntry) || directiveValueEntry.startsWith("nonce-") || directiveValueEntry.startsWith("sha256-") || directiveValueEntry.startsWith("sha384-") || directiveValueEntry.startsWith("sha512-")) {
1586
+ throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}. ${JSON.stringify(directiveValueEntry)} should be quoted`);
1587
+ }
1588
+ };
1589
+ function normalizeDirectives(options) {
1590
+ const defaultDirectives = getDefaultDirectives();
1591
+ const { useDefaults = true, directives: rawDirectives = defaultDirectives } = options;
1592
+ const result = /* @__PURE__ */ new Map();
1593
+ const directiveNamesSeen = /* @__PURE__ */ new Set();
1594
+ const directivesExplicitlyDisabled = /* @__PURE__ */ new Set();
1595
+ for (const rawDirectiveName in rawDirectives) {
1596
+ if (!Object.hasOwn(rawDirectives, rawDirectiveName)) {
1597
+ continue;
1598
+ }
1599
+ if (rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName)) {
1600
+ throw new Error(`Content-Security-Policy received an invalid directive name ${JSON.stringify(rawDirectiveName)}`);
1601
+ }
1602
+ const directiveName = dashify(rawDirectiveName);
1603
+ if (directiveNamesSeen.has(directiveName)) {
1604
+ throw new Error(`Content-Security-Policy received a duplicate directive ${JSON.stringify(directiveName)}`);
1605
+ }
1606
+ directiveNamesSeen.add(directiveName);
1607
+ const rawDirectiveValue = rawDirectives[rawDirectiveName];
1608
+ let directiveValue;
1609
+ if (rawDirectiveValue === null) {
1610
+ if (directiveName === "default-src") {
1611
+ throw new Error("Content-Security-Policy needs a default-src but it was set to `null`. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.");
1612
+ }
1613
+ directivesExplicitlyDisabled.add(directiveName);
1614
+ continue;
1615
+ } else if (typeof rawDirectiveValue === "string") {
1616
+ directiveValue = [rawDirectiveValue];
1617
+ } else if (!rawDirectiveValue) {
1618
+ throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`);
1619
+ } else if (rawDirectiveValue === dangerouslyDisableDefaultSrc) {
1620
+ if (directiveName === "default-src") {
1621
+ directivesExplicitlyDisabled.add("default-src");
1622
+ continue;
1623
+ } else {
1624
+ throw new Error(`Content-Security-Policy: tried to disable ${JSON.stringify(directiveName)} as if it were default-src; simply omit the key`);
1625
+ }
1626
+ } else {
1627
+ directiveValue = rawDirectiveValue;
1628
+ }
1629
+ for (const element of directiveValue) {
1630
+ if (typeof element !== "string") continue;
1631
+ assertDirectiveValueIsValid(directiveName, element);
1632
+ assertDirectiveValueEntryIsValid(directiveName, element);
1633
+ }
1634
+ result.set(directiveName, directiveValue);
1635
+ }
1636
+ if (useDefaults) {
1637
+ Object.entries(defaultDirectives).forEach(([defaultDirectiveName, defaultDirectiveValue]) => {
1638
+ if (!result.has(defaultDirectiveName) && !directivesExplicitlyDisabled.has(defaultDirectiveName)) {
1639
+ result.set(defaultDirectiveName, defaultDirectiveValue);
1640
+ }
1641
+ });
1642
+ }
1643
+ if (!result.size) {
1644
+ throw new Error("Content-Security-Policy has no directives. Either set some or disable the header");
1645
+ }
1646
+ if (!result.has("default-src") && !directivesExplicitlyDisabled.has("default-src")) {
1647
+ throw new Error("Content-Security-Policy needs a default-src but none was provided. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.");
1648
+ }
1649
+ return result;
1650
+ }
1651
+ function getHeaderValue(req, res, normalizedDirectives) {
1652
+ const result = [];
1653
+ for (const [directiveName, rawDirectiveValue] of normalizedDirectives) {
1654
+ let directiveValue = "";
1655
+ for (const element of rawDirectiveValue) {
1656
+ if (typeof element === "function") {
1657
+ const newElement = element(req, res);
1658
+ assertDirectiveValueEntryIsValid(directiveName, newElement);
1659
+ directiveValue += " " + newElement;
1660
+ } else {
1661
+ directiveValue += " " + element;
1662
+ }
1663
+ }
1664
+ if (directiveValue) {
1665
+ assertDirectiveValueIsValid(directiveName, directiveValue);
1666
+ result.push(`${directiveName}${directiveValue}`);
1667
+ } else {
1668
+ result.push(directiveName);
1669
+ }
1670
+ }
1671
+ return result.join(";");
1672
+ }
1673
+ var contentSecurityPolicy = function contentSecurityPolicy2(options = {}) {
1674
+ const headerName = options.reportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
1675
+ const normalizedDirectives = normalizeDirectives(options);
1676
+ return function contentSecurityPolicyMiddleware(req, res, next) {
1677
+ const result = getHeaderValue(req, res, normalizedDirectives);
1678
+ if (result instanceof Error) {
1679
+ next(result);
1680
+ } else {
1681
+ res.setHeader(headerName, result);
1682
+ next();
1683
+ }
1684
+ };
1685
+ };
1686
+ contentSecurityPolicy.getDefaultDirectives = getDefaultDirectives;
1687
+ contentSecurityPolicy.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc;
1688
+ var ALLOWED_POLICIES$2 = /* @__PURE__ */ new Set(["require-corp", "credentialless", "unsafe-none"]);
1689
+ function getHeaderValueFromOptions$6({ policy = "require-corp" }) {
1690
+ if (ALLOWED_POLICIES$2.has(policy)) {
1691
+ return policy;
1692
+ } else {
1693
+ throw new Error(`Cross-Origin-Embedder-Policy does not support the ${JSON.stringify(policy)} policy`);
1694
+ }
1695
+ }
1696
+ function crossOriginEmbedderPolicy(options = {}) {
1697
+ const headerValue = getHeaderValueFromOptions$6(options);
1698
+ return function crossOriginEmbedderPolicyMiddleware(_req, res, next) {
1699
+ res.setHeader("Cross-Origin-Embedder-Policy", headerValue);
1700
+ next();
1701
+ };
1702
+ }
1703
+ var ALLOWED_POLICIES$1 = /* @__PURE__ */ new Set(["same-origin", "same-origin-allow-popups", "unsafe-none"]);
1704
+ function getHeaderValueFromOptions$5({ policy = "same-origin" }) {
1705
+ if (ALLOWED_POLICIES$1.has(policy)) {
1706
+ return policy;
1707
+ } else {
1708
+ throw new Error(`Cross-Origin-Opener-Policy does not support the ${JSON.stringify(policy)} policy`);
1709
+ }
1710
+ }
1711
+ function crossOriginOpenerPolicy(options = {}) {
1712
+ const headerValue = getHeaderValueFromOptions$5(options);
1713
+ return function crossOriginOpenerPolicyMiddleware(_req, res, next) {
1714
+ res.setHeader("Cross-Origin-Opener-Policy", headerValue);
1715
+ next();
1716
+ };
1717
+ }
1718
+ var ALLOWED_POLICIES = /* @__PURE__ */ new Set(["same-origin", "same-site", "cross-origin"]);
1719
+ function getHeaderValueFromOptions$4({ policy = "same-origin" }) {
1720
+ if (ALLOWED_POLICIES.has(policy)) {
1721
+ return policy;
1722
+ } else {
1723
+ throw new Error(`Cross-Origin-Resource-Policy does not support the ${JSON.stringify(policy)} policy`);
1724
+ }
1725
+ }
1726
+ function crossOriginResourcePolicy(options = {}) {
1727
+ const headerValue = getHeaderValueFromOptions$4(options);
1728
+ return function crossOriginResourcePolicyMiddleware(_req, res, next) {
1729
+ res.setHeader("Cross-Origin-Resource-Policy", headerValue);
1730
+ next();
1731
+ };
1732
+ }
1733
+ function originAgentCluster() {
1734
+ return function originAgentClusterMiddleware(_req, res, next) {
1735
+ res.setHeader("Origin-Agent-Cluster", "?1");
1736
+ next();
1737
+ };
1738
+ }
1739
+ var ALLOWED_TOKENS = /* @__PURE__ */ new Set(["no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url", ""]);
1740
+ function getHeaderValueFromOptions$3({ policy = ["no-referrer"] }) {
1741
+ const tokens = typeof policy === "string" ? [policy] : policy;
1742
+ if (tokens.length === 0) {
1743
+ throw new Error("Referrer-Policy received no policy tokens");
1744
+ }
1745
+ const tokensSeen = /* @__PURE__ */ new Set();
1746
+ tokens.forEach((token) => {
1747
+ if (!ALLOWED_TOKENS.has(token)) {
1748
+ throw new Error(`Referrer-Policy received an unexpected policy token ${JSON.stringify(token)}`);
1749
+ } else if (tokensSeen.has(token)) {
1750
+ throw new Error(`Referrer-Policy received a duplicate policy token ${JSON.stringify(token)}`);
1751
+ }
1752
+ tokensSeen.add(token);
1753
+ });
1754
+ return tokens.join(",");
1755
+ }
1756
+ function referrerPolicy(options = {}) {
1757
+ const headerValue = getHeaderValueFromOptions$3(options);
1758
+ return function referrerPolicyMiddleware(_req, res, next) {
1759
+ res.setHeader("Referrer-Policy", headerValue);
1760
+ next();
1761
+ };
1762
+ }
1763
+ var DEFAULT_MAX_AGE = 365 * 24 * 60 * 60;
1764
+ function parseMaxAge(value = DEFAULT_MAX_AGE) {
1765
+ if (value >= 0 && Number.isFinite(value)) {
1766
+ return Math.floor(value);
1767
+ } else {
1768
+ throw new Error(`Strict-Transport-Security: ${JSON.stringify(value)} is not a valid value for maxAge. Please choose a positive integer.`);
1769
+ }
1770
+ }
1771
+ function getHeaderValueFromOptions$2(options) {
1772
+ if ("maxage" in options) {
1773
+ throw new Error("Strict-Transport-Security received an unsupported property, `maxage`. Did you mean to pass `maxAge`?");
1774
+ }
1775
+ if ("includeSubdomains" in options) {
1776
+ throw new Error('Strict-Transport-Security middleware should use `includeSubDomains` instead of `includeSubdomains`. (The correct one has an uppercase "D".)');
1777
+ }
1778
+ const directives = [`max-age=${parseMaxAge(options.maxAge)}`];
1779
+ if (options.includeSubDomains === void 0 || options.includeSubDomains) {
1780
+ directives.push("includeSubDomains");
1781
+ }
1782
+ if (options.preload) {
1783
+ directives.push("preload");
1784
+ }
1785
+ return directives.join("; ");
1786
+ }
1787
+ function strictTransportSecurity(options = {}) {
1788
+ const headerValue = getHeaderValueFromOptions$2(options);
1789
+ return function strictTransportSecurityMiddleware(_req, res, next) {
1790
+ res.setHeader("Strict-Transport-Security", headerValue);
1791
+ next();
1792
+ };
1793
+ }
1794
+ function xContentTypeOptions() {
1795
+ return function xContentTypeOptionsMiddleware(_req, res, next) {
1796
+ res.setHeader("X-Content-Type-Options", "nosniff");
1797
+ next();
1798
+ };
1799
+ }
1800
+ function xDnsPrefetchControl(options = {}) {
1801
+ const headerValue = options.allow ? "on" : "off";
1802
+ return function xDnsPrefetchControlMiddleware(_req, res, next) {
1803
+ res.setHeader("X-DNS-Prefetch-Control", headerValue);
1804
+ next();
1805
+ };
1806
+ }
1807
+ function xDownloadOptions() {
1808
+ return function xDownloadOptionsMiddleware(_req, res, next) {
1809
+ res.setHeader("X-Download-Options", "noopen");
1810
+ next();
1811
+ };
1812
+ }
1813
+ function getHeaderValueFromOptions$1({ action = "sameorigin" }) {
1814
+ const normalizedAction = typeof action === "string" ? action.toUpperCase() : action;
1815
+ switch (normalizedAction) {
1816
+ case "SAME-ORIGIN":
1817
+ return "SAMEORIGIN";
1818
+ case "DENY":
1819
+ case "SAMEORIGIN":
1820
+ return normalizedAction;
1821
+ default:
1822
+ throw new Error(`X-Frame-Options received an invalid action ${JSON.stringify(action)}`);
1823
+ }
1824
+ }
1825
+ function xFrameOptions(options = {}) {
1826
+ const headerValue = getHeaderValueFromOptions$1(options);
1827
+ return function xFrameOptionsMiddleware(_req, res, next) {
1828
+ res.setHeader("X-Frame-Options", headerValue);
1829
+ next();
1830
+ };
1831
+ }
1832
+ var ALLOWED_PERMITTED_POLICIES = /* @__PURE__ */ new Set(["none", "master-only", "by-content-type", "all"]);
1833
+ function getHeaderValueFromOptions({ permittedPolicies = "none" }) {
1834
+ if (ALLOWED_PERMITTED_POLICIES.has(permittedPolicies)) {
1835
+ return permittedPolicies;
1836
+ } else {
1837
+ throw new Error(`X-Permitted-Cross-Domain-Policies does not support ${JSON.stringify(permittedPolicies)}`);
1838
+ }
1839
+ }
1840
+ function xPermittedCrossDomainPolicies(options = {}) {
1841
+ const headerValue = getHeaderValueFromOptions(options);
1842
+ return function xPermittedCrossDomainPoliciesMiddleware(_req, res, next) {
1843
+ res.setHeader("X-Permitted-Cross-Domain-Policies", headerValue);
1844
+ next();
1845
+ };
1846
+ }
1847
+ function xPoweredBy() {
1848
+ return function xPoweredByMiddleware(_req, res, next) {
1849
+ res.removeHeader("X-Powered-By");
1850
+ next();
1851
+ };
1852
+ }
1853
+ function xXssProtection() {
1854
+ return function xXssProtectionMiddleware(_req, res, next) {
1855
+ res.setHeader("X-XSS-Protection", "0");
1856
+ next();
1857
+ };
1858
+ }
1859
+ function getMiddlewareFunctionsFromOptions(options) {
1860
+ const result = [];
1861
+ switch (options.contentSecurityPolicy) {
1862
+ case void 0:
1863
+ case true:
1864
+ result.push(contentSecurityPolicy());
1865
+ break;
1866
+ case false:
1867
+ break;
1868
+ default:
1869
+ result.push(contentSecurityPolicy(options.contentSecurityPolicy));
1870
+ break;
1871
+ }
1872
+ switch (options.crossOriginEmbedderPolicy) {
1873
+ case void 0:
1874
+ case false:
1875
+ break;
1876
+ case true:
1877
+ result.push(crossOriginEmbedderPolicy());
1878
+ break;
1879
+ default:
1880
+ result.push(crossOriginEmbedderPolicy(options.crossOriginEmbedderPolicy));
1881
+ break;
1882
+ }
1883
+ switch (options.crossOriginOpenerPolicy) {
1884
+ case void 0:
1885
+ case true:
1886
+ result.push(crossOriginOpenerPolicy());
1887
+ break;
1888
+ case false:
1889
+ break;
1890
+ default:
1891
+ result.push(crossOriginOpenerPolicy(options.crossOriginOpenerPolicy));
1892
+ break;
1893
+ }
1894
+ switch (options.crossOriginResourcePolicy) {
1895
+ case void 0:
1896
+ case true:
1897
+ result.push(crossOriginResourcePolicy());
1898
+ break;
1899
+ case false:
1900
+ break;
1901
+ default:
1902
+ result.push(crossOriginResourcePolicy(options.crossOriginResourcePolicy));
1903
+ break;
1904
+ }
1905
+ switch (options.originAgentCluster) {
1906
+ case void 0:
1907
+ case true:
1908
+ result.push(originAgentCluster());
1909
+ break;
1910
+ case false:
1911
+ break;
1912
+ default:
1913
+ console.warn("Origin-Agent-Cluster does not take options. Remove the property to silence this warning.");
1914
+ result.push(originAgentCluster());
1915
+ break;
1916
+ }
1917
+ switch (options.referrerPolicy) {
1918
+ case void 0:
1919
+ case true:
1920
+ result.push(referrerPolicy());
1921
+ break;
1922
+ case false:
1923
+ break;
1924
+ default:
1925
+ result.push(referrerPolicy(options.referrerPolicy));
1926
+ break;
1927
+ }
1928
+ if ("strictTransportSecurity" in options && "hsts" in options) {
1929
+ throw new Error("Strict-Transport-Security option was specified twice. Remove `hsts` to silence this warning.");
1930
+ }
1931
+ const strictTransportSecurityOption = options.strictTransportSecurity ?? options.hsts;
1932
+ switch (strictTransportSecurityOption) {
1933
+ case void 0:
1934
+ case true:
1935
+ result.push(strictTransportSecurity());
1936
+ break;
1937
+ case false:
1938
+ break;
1939
+ default:
1940
+ result.push(strictTransportSecurity(strictTransportSecurityOption));
1941
+ break;
1942
+ }
1943
+ if ("xContentTypeOptions" in options && "noSniff" in options) {
1944
+ throw new Error("X-Content-Type-Options option was specified twice. Remove `noSniff` to silence this warning.");
1945
+ }
1946
+ const xContentTypeOptionsOption = options.xContentTypeOptions ?? options.noSniff;
1947
+ switch (xContentTypeOptionsOption) {
1948
+ case void 0:
1949
+ case true:
1950
+ result.push(xContentTypeOptions());
1951
+ break;
1952
+ case false:
1953
+ break;
1954
+ default:
1955
+ console.warn("X-Content-Type-Options does not take options. Remove the property to silence this warning.");
1956
+ result.push(xContentTypeOptions());
1957
+ break;
1958
+ }
1959
+ if ("xDnsPrefetchControl" in options && "dnsPrefetchControl" in options) {
1960
+ throw new Error("X-DNS-Prefetch-Control option was specified twice. Remove `dnsPrefetchControl` to silence this warning.");
1961
+ }
1962
+ const xDnsPrefetchControlOption = options.xDnsPrefetchControl ?? options.dnsPrefetchControl;
1963
+ switch (xDnsPrefetchControlOption) {
1964
+ case void 0:
1965
+ case true:
1966
+ result.push(xDnsPrefetchControl());
1967
+ break;
1968
+ case false:
1969
+ break;
1970
+ default:
1971
+ result.push(xDnsPrefetchControl(xDnsPrefetchControlOption));
1972
+ break;
1973
+ }
1974
+ if ("xDownloadOptions" in options && "ieNoOpen" in options) {
1975
+ throw new Error("X-Download-Options option was specified twice. Remove `ieNoOpen` to silence this warning.");
1976
+ }
1977
+ const xDownloadOptionsOption = options.xDownloadOptions ?? options.ieNoOpen;
1978
+ switch (xDownloadOptionsOption) {
1979
+ case void 0:
1980
+ case true:
1981
+ result.push(xDownloadOptions());
1982
+ break;
1983
+ case false:
1984
+ break;
1985
+ default:
1986
+ console.warn("X-Download-Options does not take options. Remove the property to silence this warning.");
1987
+ result.push(xDownloadOptions());
1988
+ break;
1989
+ }
1990
+ if ("xFrameOptions" in options && "frameguard" in options) {
1991
+ throw new Error("X-Frame-Options option was specified twice. Remove `frameguard` to silence this warning.");
1992
+ }
1993
+ const xFrameOptionsOption = options.xFrameOptions ?? options.frameguard;
1994
+ switch (xFrameOptionsOption) {
1995
+ case void 0:
1996
+ case true:
1997
+ result.push(xFrameOptions());
1998
+ break;
1999
+ case false:
2000
+ break;
2001
+ default:
2002
+ result.push(xFrameOptions(xFrameOptionsOption));
2003
+ break;
2004
+ }
2005
+ if ("xPermittedCrossDomainPolicies" in options && "permittedCrossDomainPolicies" in options) {
2006
+ throw new Error("X-Permitted-Cross-Domain-Policies option was specified twice. Remove `permittedCrossDomainPolicies` to silence this warning.");
2007
+ }
2008
+ const xPermittedCrossDomainPoliciesOption = options.xPermittedCrossDomainPolicies ?? options.permittedCrossDomainPolicies;
2009
+ switch (xPermittedCrossDomainPoliciesOption) {
2010
+ case void 0:
2011
+ case true:
2012
+ result.push(xPermittedCrossDomainPolicies());
2013
+ break;
2014
+ case false:
2015
+ break;
2016
+ default:
2017
+ result.push(xPermittedCrossDomainPolicies(xPermittedCrossDomainPoliciesOption));
2018
+ break;
2019
+ }
2020
+ if ("xPoweredBy" in options && "hidePoweredBy" in options) {
2021
+ throw new Error("X-Powered-By option was specified twice. Remove `hidePoweredBy` to silence this warning.");
2022
+ }
2023
+ const xPoweredByOption = options.xPoweredBy ?? options.hidePoweredBy;
2024
+ switch (xPoweredByOption) {
2025
+ case void 0:
2026
+ case true:
2027
+ result.push(xPoweredBy());
2028
+ break;
2029
+ case false:
2030
+ break;
2031
+ default:
2032
+ console.warn("X-Powered-By does not take options. Remove the property to silence this warning.");
2033
+ result.push(xPoweredBy());
2034
+ break;
2035
+ }
2036
+ if ("xXssProtection" in options && "xssFilter" in options) {
2037
+ throw new Error("X-XSS-Protection option was specified twice. Remove `xssFilter` to silence this warning.");
2038
+ }
2039
+ const xXssProtectionOption = options.xXssProtection ?? options.xssFilter;
2040
+ switch (xXssProtectionOption) {
2041
+ case void 0:
2042
+ case true:
2043
+ result.push(xXssProtection());
2044
+ break;
2045
+ case false:
2046
+ break;
2047
+ default:
2048
+ console.warn("X-XSS-Protection does not take options. Remove the property to silence this warning.");
2049
+ result.push(xXssProtection());
2050
+ break;
2051
+ }
2052
+ return result;
2053
+ }
2054
+ var helmet = Object.assign(
2055
+ function helmet2(options = {}) {
2056
+ if (options.constructor?.name === "IncomingMessage") {
2057
+ throw new Error("It appears you have done something like `app.use(helmet)`, but it should be `app.use(helmet())`.");
2058
+ }
2059
+ const middlewareFunctions = getMiddlewareFunctionsFromOptions(options);
2060
+ return function helmetMiddleware(req, res, next) {
2061
+ let middlewareIndex = 0;
2062
+ (function internalNext(err) {
2063
+ if (err) {
2064
+ next(err);
2065
+ return;
2066
+ }
2067
+ const middlewareFunction = middlewareFunctions[middlewareIndex];
2068
+ if (middlewareFunction) {
2069
+ middlewareIndex++;
2070
+ middlewareFunction(req, res, internalNext);
2071
+ } else {
2072
+ next();
2073
+ }
2074
+ })();
2075
+ };
2076
+ },
2077
+ {
2078
+ contentSecurityPolicy,
2079
+ crossOriginEmbedderPolicy,
2080
+ crossOriginOpenerPolicy,
2081
+ crossOriginResourcePolicy,
2082
+ originAgentCluster,
2083
+ referrerPolicy,
2084
+ strictTransportSecurity,
2085
+ xContentTypeOptions,
2086
+ xDnsPrefetchControl,
2087
+ xDownloadOptions,
2088
+ xFrameOptions,
2089
+ xPermittedCrossDomainPolicies,
2090
+ xPoweredBy,
2091
+ xXssProtection,
2092
+ // Legacy aliases
2093
+ dnsPrefetchControl: xDnsPrefetchControl,
2094
+ xssFilter: xXssProtection,
2095
+ permittedCrossDomainPolicies: xPermittedCrossDomainPolicies,
2096
+ ieNoOpen: xDownloadOptions,
2097
+ noSniff: xContentTypeOptions,
2098
+ frameguard: xFrameOptions,
2099
+ hidePoweredBy: xPoweredBy,
2100
+ hsts: strictTransportSecurity
2101
+ }
2102
+ );
2103
+
2104
+ // node_modules/express-rate-limit/dist/index.mjs
2105
+ var import_ip_address = __toESM(require_ip_address(), 1);
2106
+ import { isIPv6 } from "node:net";
2107
+ import { isIPv6 as isIPv62 } from "node:net";
2108
+ import { Buffer as Buffer2 } from "node:buffer";
2109
+ import { createHash } from "node:crypto";
2110
+ import { isIP } from "node:net";
2111
+ function ipKeyGenerator(ip, ipv6Subnet = 56) {
2112
+ if (ipv6Subnet && isIPv6(ip)) {
2113
+ return `${new import_ip_address.Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;
2114
+ }
2115
+ return ip;
2116
+ }
2117
+ var MemoryStore = class {
2118
+ constructor(validations2) {
2119
+ this.validations = validations2;
2120
+ this.previous = /* @__PURE__ */ new Map();
2121
+ this.current = /* @__PURE__ */ new Map();
2122
+ this.localKeys = true;
2123
+ }
2124
+ /**
2125
+ * Method that initializes the store.
2126
+ *
2127
+ * @param options {Options} - The options used to setup the middleware.
2128
+ */
2129
+ init(options) {
2130
+ this.windowMs = options.windowMs;
2131
+ this.validations?.windowMs(this.windowMs);
2132
+ if (this.interval) clearInterval(this.interval);
2133
+ this.interval = setInterval(() => {
2134
+ this.clearExpired();
2135
+ }, this.windowMs);
2136
+ this.interval.unref?.();
2137
+ }
2138
+ /**
2139
+ * Method to fetch a client's hit count and reset time.
2140
+ *
2141
+ * @param key {string} - The identifier for a client.
2142
+ *
2143
+ * @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
2144
+ *
2145
+ * @public
2146
+ */
2147
+ async get(key) {
2148
+ return this.current.get(key) ?? this.previous.get(key);
2149
+ }
2150
+ /**
2151
+ * Method to increment a client's hit counter.
2152
+ *
2153
+ * @param key {string} - The identifier for a client.
2154
+ *
2155
+ * @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
2156
+ *
2157
+ * @public
2158
+ */
2159
+ async increment(key) {
2160
+ const client = this.getClient(key);
2161
+ const now = Date.now();
2162
+ if (client.resetTime.getTime() <= now) {
2163
+ this.resetClient(client, now);
2164
+ }
2165
+ client.totalHits++;
2166
+ return client;
2167
+ }
2168
+ /**
2169
+ * Method to decrement a client's hit counter.
2170
+ *
2171
+ * @param key {string} - The identifier for a client.
2172
+ *
2173
+ * @public
2174
+ */
2175
+ async decrement(key) {
2176
+ const client = this.getClient(key);
2177
+ if (client.totalHits > 0) client.totalHits--;
2178
+ }
2179
+ /**
2180
+ * Method to reset a client's hit counter.
2181
+ *
2182
+ * @param key {string} - The identifier for a client.
2183
+ *
2184
+ * @public
2185
+ */
2186
+ async resetKey(key) {
2187
+ this.current.delete(key);
2188
+ this.previous.delete(key);
2189
+ }
2190
+ /**
2191
+ * Method to reset everyone's hit counter.
2192
+ *
2193
+ * @public
2194
+ */
2195
+ async resetAll() {
2196
+ this.current.clear();
2197
+ this.previous.clear();
2198
+ }
2199
+ /**
2200
+ * Method to stop the timer (if currently running) and prevent any memory
2201
+ * leaks.
2202
+ *
2203
+ * @public
2204
+ */
2205
+ shutdown() {
2206
+ clearInterval(this.interval);
2207
+ void this.resetAll();
2208
+ }
2209
+ /**
2210
+ * Recycles a client by setting its hit count to zero, and reset time to
2211
+ * `windowMs` milliseconds from now.
2212
+ *
2213
+ * NOT to be confused with `#resetKey()`, which removes a client from both the
2214
+ * `current` and `previous` maps.
2215
+ *
2216
+ * @param client {Client} - The client to recycle.
2217
+ * @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
2218
+ *
2219
+ * @return {Client} - The modified client that was passed in, to allow for chaining.
2220
+ */
2221
+ resetClient(client, now = Date.now()) {
2222
+ client.totalHits = 0;
2223
+ client.resetTime.setTime(now + this.windowMs);
2224
+ return client;
2225
+ }
2226
+ /**
2227
+ * Retrieves or creates a client, given a key. Also ensures that the client being
2228
+ * returned is in the `current` map.
2229
+ *
2230
+ * @param key {string} - The key under which the client is (or is to be) stored.
2231
+ *
2232
+ * @returns {Client} - The requested client.
2233
+ */
2234
+ getClient(key) {
2235
+ if (this.current.has(key)) return this.current.get(key);
2236
+ let client;
2237
+ if (this.previous.has(key)) {
2238
+ client = this.previous.get(key);
2239
+ this.previous.delete(key);
2240
+ } else {
2241
+ client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
2242
+ this.resetClient(client);
2243
+ }
2244
+ this.current.set(key, client);
2245
+ return client;
2246
+ }
2247
+ /**
2248
+ * Move current clients to previous, create a new map for current.
2249
+ *
2250
+ * This function is called every `windowMs`.
2251
+ */
2252
+ clearExpired() {
2253
+ this.previous = this.current;
2254
+ this.current = /* @__PURE__ */ new Map();
2255
+ }
2256
+ };
2257
+ var SUPPORTED_DRAFT_VERSIONS = [
2258
+ "draft-6",
2259
+ "draft-7",
2260
+ "draft-8"
2261
+ ];
2262
+ var getResetSeconds = (windowMs, resetTime) => {
2263
+ let resetSeconds;
2264
+ if (resetTime) {
2265
+ const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);
2266
+ resetSeconds = Math.max(0, deltaSeconds);
2267
+ } else {
2268
+ resetSeconds = Math.ceil(windowMs / 1e3);
2269
+ }
2270
+ return resetSeconds;
2271
+ };
2272
+ var getPartitionKey = (key) => {
2273
+ const hash = createHash("sha256");
2274
+ hash.update(key);
2275
+ const partitionKey = hash.digest("hex").slice(0, 12);
2276
+ return Buffer2.from(partitionKey).toString("base64");
2277
+ };
2278
+ var setLegacyHeaders = (response, info) => {
2279
+ if (response.headersSent) return;
2280
+ response.setHeader("X-RateLimit-Limit", info.limit.toString());
2281
+ response.setHeader("X-RateLimit-Remaining", info.remaining.toString());
2282
+ if (info.resetTime instanceof Date) {
2283
+ response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());
2284
+ response.setHeader(
2285
+ "X-RateLimit-Reset",
2286
+ Math.ceil(info.resetTime.getTime() / 1e3).toString()
2287
+ );
2288
+ }
2289
+ };
2290
+ var setDraft6Headers = (response, info, windowMs) => {
2291
+ if (response.headersSent) return;
2292
+ const windowSeconds = Math.ceil(windowMs / 1e3);
2293
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
2294
+ response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
2295
+ response.setHeader("RateLimit-Limit", info.limit.toString());
2296
+ response.setHeader("RateLimit-Remaining", info.remaining.toString());
2297
+ if (typeof resetSeconds === "number")
2298
+ response.setHeader("RateLimit-Reset", resetSeconds.toString());
2299
+ };
2300
+ var setDraft7Headers = (response, info, windowMs) => {
2301
+ if (response.headersSent) return;
2302
+ const windowSeconds = Math.ceil(windowMs / 1e3);
2303
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
2304
+ response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
2305
+ response.setHeader(
2306
+ "RateLimit",
2307
+ `limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`
2308
+ );
2309
+ };
2310
+ var setDraft8Headers = (response, info, windowMs, name, key) => {
2311
+ if (response.headersSent) return;
2312
+ const windowSeconds = Math.ceil(windowMs / 1e3);
2313
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
2314
+ const partitionKey = getPartitionKey(key);
2315
+ const header = `r=${info.remaining}; t=${resetSeconds}`;
2316
+ const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;
2317
+ response.append("RateLimit", `"${name}"; ${header}`);
2318
+ response.append("RateLimit-Policy", `"${name}"; ${policy}`);
2319
+ };
2320
+ var setRetryAfterHeader = (response, info, windowMs) => {
2321
+ if (response.headersSent) return;
2322
+ const resetSeconds = getResetSeconds(windowMs, info.resetTime);
2323
+ response.setHeader("Retry-After", resetSeconds.toString());
2324
+ };
2325
+ var omitUndefinedProperties = (passedOptions) => {
2326
+ const omittedOptions = {};
2327
+ for (const k of Object.keys(passedOptions)) {
2328
+ const key = k;
2329
+ if (passedOptions[key] !== void 0) {
2330
+ omittedOptions[key] = passedOptions[key];
2331
+ }
2332
+ }
2333
+ return omittedOptions;
2334
+ };
2335
+ var ValidationError = class extends Error {
2336
+ /**
2337
+ * The code must be a string, in snake case and all capital, that starts with
2338
+ * the substring `ERR_ERL_`.
2339
+ *
2340
+ * The message must be a string, starting with an uppercase character,
2341
+ * describing the issue in detail.
2342
+ */
2343
+ constructor(code, message) {
2344
+ const url = `https://express-rate-limit.github.io/${code}/`;
2345
+ super(`${message} See ${url} for more information.`);
2346
+ this.name = this.constructor.name;
2347
+ this.code = code;
2348
+ this.help = url;
2349
+ }
2350
+ };
2351
+ var ChangeWarning = class extends ValidationError {
2352
+ };
2353
+ var usedStores = /* @__PURE__ */ new Set();
2354
+ var singleCountKeys = /* @__PURE__ */ new WeakMap();
2355
+ var validations = {
2356
+ enabled: {
2357
+ default: true
2358
+ },
2359
+ // Should be EnabledValidations type, but that's a circular reference
2360
+ disable() {
2361
+ for (const k of Object.keys(this.enabled)) this.enabled[k] = false;
2362
+ },
2363
+ /**
2364
+ * Checks whether the IP address is valid, and that it does not have a port
2365
+ * number in it.
2366
+ *
2367
+ * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
2368
+ *
2369
+ * @param ip {string | undefined} - The IP address provided by Express as request.ip.
2370
+ *
2371
+ * @returns {void}
2372
+ */
2373
+ ip(ip) {
2374
+ if (ip === void 0) {
2375
+ throw new ValidationError(
2376
+ "ERR_ERL_UNDEFINED_IP_ADDRESS",
2377
+ `An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
2378
+ );
2379
+ }
2380
+ if (!isIP(ip)) {
2381
+ throw new ValidationError(
2382
+ "ERR_ERL_INVALID_IP_ADDRESS",
2383
+ `An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
2384
+ );
2385
+ }
2386
+ },
2387
+ /**
2388
+ * Makes sure the trust proxy setting is not set to `true`.
2389
+ *
2390
+ * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
2391
+ *
2392
+ * @param request {Request} - The Express request object.
2393
+ *
2394
+ * @returns {void}
2395
+ */
2396
+ trustProxy(request) {
2397
+ if (request.app.get("trust proxy") === true) {
2398
+ throw new ValidationError(
2399
+ "ERR_ERL_PERMISSIVE_TRUST_PROXY",
2400
+ `The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`
2401
+ );
2402
+ }
2403
+ },
2404
+ /**
2405
+ * Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
2406
+ * header is present.
2407
+ *
2408
+ * See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
2409
+ *
2410
+ * @param request {Request} - The Express request object.
2411
+ *
2412
+ * @returns {void}
2413
+ */
2414
+ xForwardedForHeader(request) {
2415
+ if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {
2416
+ throw new ValidationError(
2417
+ "ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",
2418
+ `The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`
2419
+ );
2420
+ }
2421
+ },
2422
+ /**
2423
+ * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
2424
+ *
2425
+ * @param request {Request} - The Express request object.
2426
+ *
2427
+ * @returns {void}
2428
+ */
2429
+ forwardedHeader(request) {
2430
+ if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
2431
+ throw new ValidationError(
2432
+ "ERR_ERL_FORWARDED_HEADER",
2433
+ `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
2434
+ );
2435
+ }
2436
+ },
2437
+ /**
2438
+ * Ensures totalHits value from store is a positive integer.
2439
+ *
2440
+ * @param hits {any} - The `totalHits` returned by the store.
2441
+ */
2442
+ positiveHits(hits) {
2443
+ if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {
2444
+ throw new ValidationError(
2445
+ "ERR_ERL_INVALID_HITS",
2446
+ `The totalHits value returned from the store must be a positive integer, got ${hits}`
2447
+ );
2448
+ }
2449
+ },
2450
+ /**
2451
+ * Ensures a single store instance is not used with multiple express-rate-limit instances
2452
+ */
2453
+ unsharedStore(store) {
2454
+ if (usedStores.has(store)) {
2455
+ const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";
2456
+ throw new ValidationError(
2457
+ "ERR_ERL_STORE_REUSE",
2458
+ `A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`
2459
+ );
2460
+ }
2461
+ usedStores.add(store);
2462
+ },
2463
+ /**
2464
+ * Ensures a given key is incremented only once per request.
2465
+ *
2466
+ * @param request {Request} - The Express request object.
2467
+ * @param store {Store} - The store class.
2468
+ * @param key {string} - The key used to store the client's hit count.
2469
+ *
2470
+ * @returns {void}
2471
+ */
2472
+ singleCount(request, store, key) {
2473
+ let storeKeys = singleCountKeys.get(request);
2474
+ if (!storeKeys) {
2475
+ storeKeys = /* @__PURE__ */ new Map();
2476
+ singleCountKeys.set(request, storeKeys);
2477
+ }
2478
+ const storeKey = store.localKeys ? store : store.constructor.name;
2479
+ let keys = storeKeys.get(storeKey);
2480
+ if (!keys) {
2481
+ keys = [];
2482
+ storeKeys.set(storeKey, keys);
2483
+ }
2484
+ const prefixedKey = `${store.prefix ?? ""}${key}`;
2485
+ if (keys.includes(prefixedKey)) {
2486
+ throw new ValidationError(
2487
+ "ERR_ERL_DOUBLE_COUNT",
2488
+ `The hit count for ${key} was incremented more than once for a single request.`
2489
+ );
2490
+ }
2491
+ keys.push(prefixedKey);
2492
+ },
2493
+ /**
2494
+ * Warns the user that the behaviour for `max: 0` / `limit: 0` is
2495
+ * changing in the next major release.
2496
+ *
2497
+ * @param limit {number} - The maximum number of hits per client.
2498
+ *
2499
+ * @returns {void}
2500
+ */
2501
+ limit(limit) {
2502
+ if (limit === 0) {
2503
+ throw new ChangeWarning(
2504
+ "WRN_ERL_MAX_ZERO",
2505
+ "Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7"
2506
+ );
2507
+ }
2508
+ },
2509
+ /**
2510
+ * Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
2511
+ * and will be removed in the next major release.
2512
+ *
2513
+ * @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
2514
+ *
2515
+ * @returns {void}
2516
+ */
2517
+ draftPolliHeaders(draft_polli_ratelimit_headers) {
2518
+ if (draft_polli_ratelimit_headers) {
2519
+ throw new ChangeWarning(
2520
+ "WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",
2521
+ `The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`
2522
+ );
2523
+ }
2524
+ },
2525
+ /**
2526
+ * Warns the user that the `onLimitReached` option is deprecated and
2527
+ * will be removed in the next major release.
2528
+ *
2529
+ * @param onLimitReached {any | undefined} - The maximum number of hits per client.
2530
+ *
2531
+ * @returns {void}
2532
+ */
2533
+ onLimitReached(onLimitReached) {
2534
+ if (onLimitReached) {
2535
+ throw new ChangeWarning(
2536
+ "WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
2537
+ "The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7."
2538
+ );
2539
+ }
2540
+ },
2541
+ /**
2542
+ * Warns the user when an invalid/unsupported version of the draft spec is passed.
2543
+ *
2544
+ * @param version {any | undefined} - The version passed by the user.
2545
+ *
2546
+ * @returns {void}
2547
+ */
2548
+ headersDraftVersion(version) {
2549
+ if (typeof version !== "string" || // @ts-expect-error This is fine. If version is not in the array, it will just return false.
2550
+ !SUPPORTED_DRAFT_VERSIONS.includes(version)) {
2551
+ const versionString = SUPPORTED_DRAFT_VERSIONS.join(", ");
2552
+ throw new ValidationError(
2553
+ "ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",
2554
+ `standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`
2555
+ );
2556
+ }
2557
+ },
2558
+ /**
2559
+ * Warns the user when the selected headers option requires a reset time but
2560
+ * the store does not provide one.
2561
+ *
2562
+ * @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
2563
+ *
2564
+ * @returns {void}
2565
+ */
2566
+ headersResetTime(resetTime) {
2567
+ if (!resetTime) {
2568
+ throw new ValidationError(
2569
+ "ERR_ERL_HEADERS_NO_RESET",
2570
+ `standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`
2571
+ );
2572
+ }
2573
+ },
2574
+ knownOptions(passedOptions) {
2575
+ if (!passedOptions) return;
2576
+ const optionsMap = {
2577
+ windowMs: true,
2578
+ limit: true,
2579
+ message: true,
2580
+ statusCode: true,
2581
+ legacyHeaders: true,
2582
+ standardHeaders: true,
2583
+ identifier: true,
2584
+ requestPropertyName: true,
2585
+ skipFailedRequests: true,
2586
+ skipSuccessfulRequests: true,
2587
+ keyGenerator: true,
2588
+ ipv6Subnet: true,
2589
+ handler: true,
2590
+ skip: true,
2591
+ requestWasSuccessful: true,
2592
+ store: true,
2593
+ validate: true,
2594
+ headers: true,
2595
+ max: true,
2596
+ passOnStoreError: true
2597
+ };
2598
+ const validOptions = Object.keys(optionsMap).concat(
2599
+ "draft_polli_ratelimit_headers",
2600
+ // not a valid option anymore, but we have a more specific check for this one, so don't warn for it here
2601
+ // from express-slow-down - https://github.com/express-rate-limit/express-slow-down/blob/main/source/types.ts#L65
2602
+ "delayAfter",
2603
+ "delayMs",
2604
+ "maxDelayMs"
2605
+ );
2606
+ for (const key of Object.keys(passedOptions)) {
2607
+ if (!validOptions.includes(key)) {
2608
+ throw new ValidationError(
2609
+ "ERR_ERL_UNKNOWN_OPTION",
2610
+ `Unexpected configuration option: ${key}`
2611
+ // todo: suggest a valid option with a short levenstein distance?
2612
+ );
2613
+ }
2614
+ }
2615
+ },
2616
+ /**
2617
+ * Checks the options.validate setting to ensure that only recognized
2618
+ * validations are enabled or disabled.
2619
+ *
2620
+ * If any unrecognized values are found, an error is logged that
2621
+ * includes the list of supported validations.
2622
+ */
2623
+ validationsConfig() {
2624
+ const supportedValidations = Object.keys(this).filter(
2625
+ (k) => !["enabled", "disable"].includes(k)
2626
+ );
2627
+ supportedValidations.push("default");
2628
+ for (const key of Object.keys(this.enabled)) {
2629
+ if (!supportedValidations.includes(key)) {
2630
+ throw new ValidationError(
2631
+ "ERR_ERL_UNKNOWN_VALIDATION",
2632
+ `options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(
2633
+ ", "
2634
+ )}.`
2635
+ );
2636
+ }
2637
+ }
2638
+ },
2639
+ /**
2640
+ * Checks to see if the instance was created inside of a request handler,
2641
+ * which would prevent it from working correctly, with the default memory
2642
+ * store (or any other store with localKeys.)
2643
+ */
2644
+ creationStack(store) {
2645
+ const { stack } = new Error(
2646
+ "express-rate-limit validation check (set options.validate.creationStack=false to disable)"
2647
+ );
2648
+ if (stack?.includes("Layer.handle [as handle_request]") || // express v4
2649
+ stack?.includes("Layer.handleRequest")) {
2650
+ if (!store.localKeys) {
2651
+ throw new ValidationError(
2652
+ "ERR_ERL_CREATED_IN_REQUEST_HANDLER",
2653
+ "express-rate-limit instance should *usually* be created at app initialization, not when responding to a request."
2654
+ );
2655
+ }
2656
+ throw new ValidationError(
2657
+ "ERR_ERL_CREATED_IN_REQUEST_HANDLER",
2658
+ "express-rate-limit instance should be created at app initialization, not when responding to a request."
2659
+ );
2660
+ }
2661
+ },
2662
+ ipv6Subnet(ipv6Subnet) {
2663
+ if (ipv6Subnet === false) {
2664
+ return;
2665
+ }
2666
+ if (!Number.isInteger(ipv6Subnet) || ipv6Subnet < 32 || ipv6Subnet > 64) {
2667
+ throw new ValidationError(
2668
+ "ERR_ERL_IPV6_SUBNET",
2669
+ `Unexpected ipv6Subnet value: ${ipv6Subnet}. Expected an integer between 32 and 64 (usually 48-64).`
2670
+ );
2671
+ }
2672
+ },
2673
+ ipv6SubnetOrKeyGenerator(options) {
2674
+ if (options.ipv6Subnet !== void 0 && options.keyGenerator) {
2675
+ throw new ValidationError(
2676
+ "ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR",
2677
+ `Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.`
2678
+ );
2679
+ }
2680
+ },
2681
+ keyGeneratorIpFallback(keyGenerator) {
2682
+ if (!keyGenerator) {
2683
+ return;
2684
+ }
2685
+ const src = keyGenerator.toString();
2686
+ if ((src.includes("req.ip") || src.includes("request.ip")) && !src.includes("ipKeyGenerator")) {
2687
+ throw new ValidationError(
2688
+ "ERR_ERL_KEY_GEN_IPV6",
2689
+ "Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
2690
+ );
2691
+ }
2692
+ },
2693
+ /**
2694
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
2695
+ * called by the default MemoryStore, since it uses Node's setInterval method.
2696
+ *
2697
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
2698
+ */
2699
+ windowMs(windowMs) {
2700
+ const SET_TIMEOUT_MAX = 2 ** 31 - 1;
2701
+ if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
2702
+ throw new ValidationError(
2703
+ "ERR_ERL_WINDOW_MS",
2704
+ `Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
2705
+ );
2706
+ }
2707
+ }
2708
+ };
2709
+ var getValidations = (_enabled) => {
2710
+ let enabled;
2711
+ if (typeof _enabled === "boolean") {
2712
+ enabled = {
2713
+ default: _enabled
2714
+ };
2715
+ } else {
2716
+ enabled = {
2717
+ default: true,
2718
+ ..._enabled
2719
+ };
2720
+ }
2721
+ const wrappedValidations = { enabled };
2722
+ for (const [name, validation] of Object.entries(validations)) {
2723
+ if (typeof validation === "function")
2724
+ wrappedValidations[name] = (...args) => {
2725
+ if (!(enabled[name] ?? enabled.default)) {
2726
+ return;
2727
+ }
2728
+ try {
2729
+ ;
2730
+ validation.apply(
2731
+ wrappedValidations,
2732
+ args
2733
+ );
2734
+ } catch (error) {
2735
+ if (error instanceof ChangeWarning) console.warn(error);
2736
+ else console.error(error);
2737
+ }
2738
+ };
2739
+ }
2740
+ return wrappedValidations;
2741
+ };
2742
+ var isLegacyStore = (store) => (
2743
+ // Check that `incr` exists but `increment` does not - store authors might want
2744
+ // to keep both around for backwards compatibility.
2745
+ typeof store.incr === "function" && typeof store.increment !== "function"
2746
+ );
2747
+ var promisifyStore = (passedStore) => {
2748
+ if (!isLegacyStore(passedStore)) {
2749
+ return passedStore;
2750
+ }
2751
+ const legacyStore = passedStore;
2752
+ class PromisifiedStore {
2753
+ async increment(key) {
2754
+ return new Promise((resolve, reject) => {
2755
+ legacyStore.incr(
2756
+ key,
2757
+ (error, totalHits, resetTime) => {
2758
+ if (error) reject(error);
2759
+ resolve({ totalHits, resetTime });
2760
+ }
2761
+ );
2762
+ });
2763
+ }
2764
+ async decrement(key) {
2765
+ return legacyStore.decrement(key);
2766
+ }
2767
+ async resetKey(key) {
2768
+ return legacyStore.resetKey(key);
2769
+ }
2770
+ /* istanbul ignore next */
2771
+ async resetAll() {
2772
+ if (typeof legacyStore.resetAll === "function")
2773
+ return legacyStore.resetAll();
2774
+ }
2775
+ }
2776
+ return new PromisifiedStore();
2777
+ };
2778
+ var getOptionsFromConfig = (config) => {
2779
+ const { validations: validations2, ...directlyPassableEntries } = config;
2780
+ return {
2781
+ ...directlyPassableEntries,
2782
+ validate: validations2.enabled
269
2783
  };
270
- }
271
- var init_Search = __esm({
272
- "src/services/sqlite/Search.ts"() {
273
- "use strict";
2784
+ };
2785
+ var parseOptions = (passedOptions) => {
2786
+ const notUndefinedOptions = omitUndefinedProperties(passedOptions);
2787
+ const validations2 = getValidations(notUndefinedOptions?.validate ?? true);
2788
+ validations2.validationsConfig();
2789
+ validations2.knownOptions(passedOptions);
2790
+ validations2.draftPolliHeaders(
2791
+ // @ts-expect-error see the note above.
2792
+ notUndefinedOptions.draft_polli_ratelimit_headers
2793
+ );
2794
+ validations2.onLimitReached(notUndefinedOptions.onLimitReached);
2795
+ if (notUndefinedOptions.ipv6Subnet !== void 0 && typeof notUndefinedOptions.ipv6Subnet !== "function") {
2796
+ validations2.ipv6Subnet(notUndefinedOptions.ipv6Subnet);
274
2797
  }
275
- });
2798
+ validations2.keyGeneratorIpFallback(notUndefinedOptions.keyGenerator);
2799
+ validations2.ipv6SubnetOrKeyGenerator(notUndefinedOptions);
2800
+ let standardHeaders = notUndefinedOptions.standardHeaders ?? false;
2801
+ if (standardHeaders === true) standardHeaders = "draft-6";
2802
+ const config = {
2803
+ windowMs: 60 * 1e3,
2804
+ limit: passedOptions.max ?? 5,
2805
+ // `max` is deprecated, but support it anyways.
2806
+ message: "Too many requests, please try again later.",
2807
+ statusCode: 429,
2808
+ legacyHeaders: passedOptions.headers ?? true,
2809
+ identifier(request, _response) {
2810
+ let duration = "";
2811
+ const property = config.requestPropertyName;
2812
+ const { limit } = request[property];
2813
+ const seconds = config.windowMs / 1e3;
2814
+ const minutes = config.windowMs / (1e3 * 60);
2815
+ const hours = config.windowMs / (1e3 * 60 * 60);
2816
+ const days = config.windowMs / (1e3 * 60 * 60 * 24);
2817
+ if (seconds < 60) duration = `${seconds}sec`;
2818
+ else if (minutes < 60) duration = `${minutes}min`;
2819
+ else if (hours < 24) duration = `${hours}hr${hours > 1 ? "s" : ""}`;
2820
+ else duration = `${days}day${days > 1 ? "s" : ""}`;
2821
+ return `${limit}-in-${duration}`;
2822
+ },
2823
+ requestPropertyName: "rateLimit",
2824
+ skipFailedRequests: false,
2825
+ skipSuccessfulRequests: false,
2826
+ requestWasSuccessful: (_request, response) => response.statusCode < 400,
2827
+ skip: (_request, _response) => false,
2828
+ async keyGenerator(request, response) {
2829
+ validations2.ip(request.ip);
2830
+ validations2.trustProxy(request);
2831
+ validations2.xForwardedForHeader(request);
2832
+ validations2.forwardedHeader(request);
2833
+ const ip = request.ip;
2834
+ let subnet = 56;
2835
+ if (isIPv62(ip)) {
2836
+ subnet = typeof config.ipv6Subnet === "function" ? await config.ipv6Subnet(request, response) : config.ipv6Subnet;
2837
+ if (typeof config.ipv6Subnet === "function")
2838
+ validations2.ipv6Subnet(subnet);
2839
+ }
2840
+ return ipKeyGenerator(ip, subnet);
2841
+ },
2842
+ ipv6Subnet: 56,
2843
+ async handler(request, response, _next, _optionsUsed) {
2844
+ response.status(config.statusCode);
2845
+ const message = typeof config.message === "function" ? await config.message(
2846
+ request,
2847
+ response
2848
+ ) : config.message;
2849
+ if (!response.writableEnded) response.send(message);
2850
+ },
2851
+ passOnStoreError: false,
2852
+ // Allow the default options to be overridden by the passed options.
2853
+ ...notUndefinedOptions,
2854
+ // `standardHeaders` is resolved into a draft version above, use that.
2855
+ standardHeaders,
2856
+ // Note that this field is declared after the user's options are spread in,
2857
+ // so that this field doesn't get overridden with an un-promisified store!
2858
+ store: promisifyStore(
2859
+ notUndefinedOptions.store ?? new MemoryStore(validations2)
2860
+ ),
2861
+ // Print an error to the console if a few known misconfigurations are detected.
2862
+ validations: validations2
2863
+ };
2864
+ if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {
2865
+ throw new TypeError(
2866
+ "An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
2867
+ );
2868
+ }
2869
+ return config;
2870
+ };
2871
+ var handleAsyncErrors = (fn) => async (request, response, next) => {
2872
+ try {
2873
+ await Promise.resolve(fn(request, response, next)).catch(next);
2874
+ } catch (error) {
2875
+ next(error);
2876
+ }
2877
+ };
2878
+ var rateLimit = (passedOptions) => {
2879
+ const config = parseOptions(passedOptions ?? {});
2880
+ const options = getOptionsFromConfig(config);
2881
+ config.validations.creationStack(config.store);
2882
+ config.validations.unsharedStore(config.store);
2883
+ if (typeof config.store.init === "function") config.store.init(options);
2884
+ const middleware = handleAsyncErrors(
2885
+ async (request, response, next) => {
2886
+ const skip = await config.skip(request, response);
2887
+ if (skip) {
2888
+ next();
2889
+ return;
2890
+ }
2891
+ const augmentedRequest = request;
2892
+ const key = await config.keyGenerator(request, response);
2893
+ let totalHits = 0;
2894
+ let resetTime;
2895
+ try {
2896
+ const incrementResult = await config.store.increment(key);
2897
+ totalHits = incrementResult.totalHits;
2898
+ resetTime = incrementResult.resetTime;
2899
+ } catch (error) {
2900
+ if (config.passOnStoreError) {
2901
+ console.error(
2902
+ "express-rate-limit: error from store, allowing request without rate-limiting.",
2903
+ error
2904
+ );
2905
+ next();
2906
+ return;
2907
+ }
2908
+ throw error;
2909
+ }
2910
+ config.validations.positiveHits(totalHits);
2911
+ config.validations.singleCount(request, config.store, key);
2912
+ const retrieveLimit = typeof config.limit === "function" ? config.limit(request, response) : config.limit;
2913
+ const limit = await retrieveLimit;
2914
+ config.validations.limit(limit);
2915
+ const info = {
2916
+ limit,
2917
+ used: totalHits,
2918
+ remaining: Math.max(limit - totalHits, 0),
2919
+ resetTime,
2920
+ key
2921
+ };
2922
+ Object.defineProperty(info, "current", {
2923
+ configurable: false,
2924
+ enumerable: false,
2925
+ value: totalHits
2926
+ });
2927
+ augmentedRequest[config.requestPropertyName] = info;
2928
+ if (config.legacyHeaders && !response.headersSent) {
2929
+ setLegacyHeaders(response, info);
2930
+ }
2931
+ if (config.standardHeaders && !response.headersSent) {
2932
+ switch (config.standardHeaders) {
2933
+ case "draft-6": {
2934
+ setDraft6Headers(response, info, config.windowMs);
2935
+ break;
2936
+ }
2937
+ case "draft-7": {
2938
+ config.validations.headersResetTime(info.resetTime);
2939
+ setDraft7Headers(response, info, config.windowMs);
2940
+ break;
2941
+ }
2942
+ case "draft-8": {
2943
+ const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;
2944
+ const name = await retrieveName;
2945
+ config.validations.headersResetTime(info.resetTime);
2946
+ setDraft8Headers(response, info, config.windowMs, name, key);
2947
+ break;
2948
+ }
2949
+ default: {
2950
+ config.validations.headersDraftVersion(config.standardHeaders);
2951
+ break;
2952
+ }
2953
+ }
2954
+ }
2955
+ if (config.skipFailedRequests || config.skipSuccessfulRequests) {
2956
+ let decremented = false;
2957
+ const decrementKey = async () => {
2958
+ if (!decremented) {
2959
+ await config.store.decrement(key);
2960
+ decremented = true;
2961
+ }
2962
+ };
2963
+ if (config.skipFailedRequests) {
2964
+ response.on("finish", async () => {
2965
+ if (!await config.requestWasSuccessful(request, response))
2966
+ await decrementKey();
2967
+ });
2968
+ response.on("close", async () => {
2969
+ if (!response.writableEnded) await decrementKey();
2970
+ });
2971
+ response.on("error", async () => {
2972
+ await decrementKey();
2973
+ });
2974
+ }
2975
+ if (config.skipSuccessfulRequests) {
2976
+ response.on("finish", async () => {
2977
+ if (await config.requestWasSuccessful(request, response))
2978
+ await decrementKey();
2979
+ });
2980
+ }
2981
+ }
2982
+ config.validations.disable();
2983
+ if (totalHits > limit) {
2984
+ if (config.legacyHeaders || config.standardHeaders) {
2985
+ setRetryAfterHeader(response, info, config.windowMs);
2986
+ }
2987
+ config.handler(request, response, next, options);
2988
+ return;
2989
+ }
2990
+ next();
2991
+ }
2992
+ );
2993
+ const getThrowFn = () => {
2994
+ throw new Error("The current store does not support the get/getKey method");
2995
+ };
2996
+ middleware.resetKey = config.store.resetKey.bind(config.store);
2997
+ middleware.getKey = typeof config.store.get === "function" ? config.store.get.bind(config.store) : getThrowFn;
2998
+ return middleware;
2999
+ };
3000
+ var rate_limit_default = rateLimit;
276
3001
 
277
3002
  // src/services/worker-service.ts
278
- import express from "express";
279
- import cors from "cors";
3003
+ import crypto from "crypto";
280
3004
  import { join as join3, dirname as dirname2 } from "path";
281
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync, unlinkSync } from "fs";
3005
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync, unlinkSync, chmodSync } from "fs";
282
3006
  import { fileURLToPath as fileURLToPath2 } from "url";
283
3007
 
284
3008
  // src/shims/bun-sqlite.ts
@@ -351,7 +3075,7 @@ var BunQueryCompat = class {
351
3075
  // src/shared/paths.ts
352
3076
  import { join as join2, dirname, basename } from "path";
353
3077
  import { homedir as homedir2 } from "os";
354
- import { mkdirSync as mkdirSync2 } from "fs";
3078
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
355
3079
  import { fileURLToPath } from "url";
356
3080
 
357
3081
  // src/utils/logger.ts
@@ -581,7 +3305,9 @@ function getDirname() {
581
3305
  return dirname(fileURLToPath(import.meta.url));
582
3306
  }
583
3307
  var _dirname = getDirname();
584
- var DATA_DIR = process.env.KIRO_MEMORY_DATA_DIR || process.env.CONTEXTKIT_DATA_DIR || join2(homedir2(), ".contextkit");
3308
+ var _legacyDir = join2(homedir2(), ".contextkit");
3309
+ var _defaultDir = existsSync2(_legacyDir) ? _legacyDir : join2(homedir2(), ".kiro-memory");
3310
+ var DATA_DIR = process.env.KIRO_MEMORY_DATA_DIR || process.env.CONTEXTKIT_DATA_DIR || _defaultDir;
585
3311
  var KIRO_CONFIG_DIR = process.env.KIRO_CONFIG_DIR || join2(homedir2(), ".kiro");
586
3312
  var PLUGIN_ROOT = join2(KIRO_CONFIG_DIR, "plugins", "kiro-memory");
587
3313
  var ARCHIVES_DIR = join2(DATA_DIR, "archives");
@@ -590,7 +3316,8 @@ var TRASH_DIR = join2(DATA_DIR, "trash");
590
3316
  var BACKUPS_DIR = join2(DATA_DIR, "backups");
591
3317
  var MODES_DIR = join2(DATA_DIR, "modes");
592
3318
  var USER_SETTINGS_PATH = join2(DATA_DIR, "settings.json");
593
- var DB_PATH = join2(DATA_DIR, "contextkit.db");
3319
+ var _legacyDb = join2(DATA_DIR, "contextkit.db");
3320
+ var DB_PATH = existsSync2(_legacyDb) ? _legacyDb : join2(DATA_DIR, "kiro-memory.db");
594
3321
  var VECTOR_DB_DIR = join2(DATA_DIR, "vector-db");
595
3322
  var OBSERVER_SESSIONS_DIR = join2(DATA_DIR, "observer-sessions");
596
3323
  var KIRO_SETTINGS_PATH = join2(KIRO_CONFIG_DIR, "settings.json");
@@ -604,7 +3331,11 @@ var SQLITE_MMAP_SIZE_BYTES = 256 * 1024 * 1024;
604
3331
  var SQLITE_CACHE_SIZE_PAGES = 1e4;
605
3332
  var KiroMemoryDatabase = class {
606
3333
  db;
607
- constructor(dbPath = DB_PATH) {
3334
+ /**
3335
+ * @param dbPath - Percorso al file SQLite (default: DB_PATH)
3336
+ * @param skipMigrations - Se true, salta il migration runner (per hook ad alta frequenza)
3337
+ */
3338
+ constructor(dbPath = DB_PATH, skipMigrations = false) {
608
3339
  if (dbPath !== ":memory:") {
609
3340
  ensureDir(DATA_DIR);
610
3341
  }
@@ -615,8 +3346,18 @@ var KiroMemoryDatabase = class {
615
3346
  this.db.run("PRAGMA temp_store = memory");
616
3347
  this.db.run(`PRAGMA mmap_size = ${SQLITE_MMAP_SIZE_BYTES}`);
617
3348
  this.db.run(`PRAGMA cache_size = ${SQLITE_CACHE_SIZE_PAGES}`);
618
- const migrationRunner = new MigrationRunner(this.db);
619
- migrationRunner.runAllMigrations();
3349
+ if (!skipMigrations) {
3350
+ const migrationRunner = new MigrationRunner(this.db);
3351
+ migrationRunner.runAllMigrations();
3352
+ }
3353
+ }
3354
+ /**
3355
+ * Esegue una funzione all'interno di una transazione atomica.
3356
+ * Se fn() lancia un errore, la transazione viene annullata automaticamente.
3357
+ */
3358
+ withTransaction(fn) {
3359
+ const transaction = this.db.transaction(fn);
3360
+ return transaction(this.db);
620
3361
  }
621
3362
  /**
622
3363
  * Close the database connection
@@ -796,19 +3537,245 @@ var MigrationRunner = class {
796
3537
  }
797
3538
  };
798
3539
 
3540
+ // src/services/sqlite/Observations.ts
3541
+ function createObservation(db2, memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber) {
3542
+ const now = /* @__PURE__ */ new Date();
3543
+ const result = db2.run(
3544
+ `INSERT INTO observations
3545
+ (memory_session_id, project, type, title, subtitle, text, narrative, facts, concepts, files_read, files_modified, prompt_number, created_at, created_at_epoch)
3546
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
3547
+ [memorySessionId, project, type, title, subtitle, text, narrative, facts, concepts, filesRead, filesModified, promptNumber, now.toISOString(), now.getTime()]
3548
+ );
3549
+ return Number(result.lastInsertRowid);
3550
+ }
3551
+ function getObservationsByProject(db2, project, limit = 100) {
3552
+ const query = db2.query(
3553
+ "SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
3554
+ );
3555
+ return query.all(project, limit);
3556
+ }
3557
+
3558
+ // src/services/sqlite/Summaries.ts
3559
+ function createSummary(db2, sessionId, project, request, investigated, learned, completed, nextSteps, notes) {
3560
+ const now = /* @__PURE__ */ new Date();
3561
+ const result = db2.run(
3562
+ `INSERT INTO summaries
3563
+ (session_id, project, request, investigated, learned, completed, next_steps, notes, created_at, created_at_epoch)
3564
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
3565
+ [sessionId, project, request, investigated, learned, completed, nextSteps, notes, now.toISOString(), now.getTime()]
3566
+ );
3567
+ return Number(result.lastInsertRowid);
3568
+ }
3569
+ function getSummariesByProject(db2, project, limit = 50) {
3570
+ const query = db2.query(
3571
+ "SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ?"
3572
+ );
3573
+ return query.all(project, limit);
3574
+ }
3575
+
3576
+ // src/services/sqlite/Search.ts
3577
+ function sanitizeFTS5Query(query) {
3578
+ const terms = query.replace(/[""]/g, "").split(/\s+/).filter((t) => t.length > 0).map((t) => `"${t}"`);
3579
+ return terms.join(" ");
3580
+ }
3581
+ function searchObservationsFTS(db2, query, filters = {}) {
3582
+ const limit = filters.limit || 50;
3583
+ try {
3584
+ const safeQuery = sanitizeFTS5Query(query);
3585
+ if (!safeQuery) return searchObservationsLIKE(db2, query, filters);
3586
+ let sql = `
3587
+ SELECT o.* FROM observations o
3588
+ JOIN observations_fts fts ON o.id = fts.rowid
3589
+ WHERE observations_fts MATCH ?
3590
+ `;
3591
+ const params = [safeQuery];
3592
+ if (filters.project) {
3593
+ sql += " AND o.project = ?";
3594
+ params.push(filters.project);
3595
+ }
3596
+ if (filters.type) {
3597
+ sql += " AND o.type = ?";
3598
+ params.push(filters.type);
3599
+ }
3600
+ if (filters.dateStart) {
3601
+ sql += " AND o.created_at_epoch >= ?";
3602
+ params.push(filters.dateStart);
3603
+ }
3604
+ if (filters.dateEnd) {
3605
+ sql += " AND o.created_at_epoch <= ?";
3606
+ params.push(filters.dateEnd);
3607
+ }
3608
+ sql += " ORDER BY rank LIMIT ?";
3609
+ params.push(limit);
3610
+ const stmt = db2.query(sql);
3611
+ return stmt.all(...params);
3612
+ } catch {
3613
+ return searchObservationsLIKE(db2, query, filters);
3614
+ }
3615
+ }
3616
+ function searchObservationsLIKE(db2, query, filters = {}) {
3617
+ const limit = filters.limit || 50;
3618
+ const pattern = `%${query}%`;
3619
+ let sql = `
3620
+ SELECT * FROM observations
3621
+ WHERE (title LIKE ? OR text LIKE ? OR narrative LIKE ? OR concepts LIKE ?)
3622
+ `;
3623
+ const params = [pattern, pattern, pattern, pattern];
3624
+ if (filters.project) {
3625
+ sql += " AND project = ?";
3626
+ params.push(filters.project);
3627
+ }
3628
+ if (filters.type) {
3629
+ sql += " AND type = ?";
3630
+ params.push(filters.type);
3631
+ }
3632
+ if (filters.dateStart) {
3633
+ sql += " AND created_at_epoch >= ?";
3634
+ params.push(filters.dateStart);
3635
+ }
3636
+ if (filters.dateEnd) {
3637
+ sql += " AND created_at_epoch <= ?";
3638
+ params.push(filters.dateEnd);
3639
+ }
3640
+ sql += " ORDER BY created_at_epoch DESC LIMIT ?";
3641
+ params.push(limit);
3642
+ const stmt = db2.query(sql);
3643
+ return stmt.all(...params);
3644
+ }
3645
+ function searchSummariesFiltered(db2, query, filters = {}) {
3646
+ const limit = filters.limit || 20;
3647
+ const pattern = `%${query}%`;
3648
+ let sql = `
3649
+ SELECT * FROM summaries
3650
+ WHERE (request LIKE ? OR learned LIKE ? OR completed LIKE ? OR notes LIKE ? OR next_steps LIKE ?)
3651
+ `;
3652
+ const params = [pattern, pattern, pattern, pattern, pattern];
3653
+ if (filters.project) {
3654
+ sql += " AND project = ?";
3655
+ params.push(filters.project);
3656
+ }
3657
+ if (filters.dateStart) {
3658
+ sql += " AND created_at_epoch >= ?";
3659
+ params.push(filters.dateStart);
3660
+ }
3661
+ if (filters.dateEnd) {
3662
+ sql += " AND created_at_epoch <= ?";
3663
+ params.push(filters.dateEnd);
3664
+ }
3665
+ sql += " ORDER BY created_at_epoch DESC LIMIT ?";
3666
+ params.push(limit);
3667
+ const stmt = db2.query(sql);
3668
+ return stmt.all(...params);
3669
+ }
3670
+ function getObservationsByIds(db2, ids) {
3671
+ if (ids.length === 0) return [];
3672
+ const placeholders = ids.map(() => "?").join(",");
3673
+ const sql = `SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch DESC`;
3674
+ const stmt = db2.query(sql);
3675
+ return stmt.all(...ids);
3676
+ }
3677
+ function getTimeline(db2, anchorId, depthBefore = 5, depthAfter = 5) {
3678
+ const anchorStmt = db2.query("SELECT created_at_epoch FROM observations WHERE id = ?");
3679
+ const anchor = anchorStmt.get(anchorId);
3680
+ if (!anchor) return [];
3681
+ const anchorEpoch = anchor.created_at_epoch;
3682
+ const beforeStmt = db2.query(`
3683
+ SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
3684
+ FROM observations
3685
+ WHERE created_at_epoch < ?
3686
+ ORDER BY created_at_epoch DESC
3687
+ LIMIT ?
3688
+ `);
3689
+ const before = beforeStmt.all(anchorEpoch, depthBefore).reverse();
3690
+ const selfStmt = db2.query(`
3691
+ SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
3692
+ FROM observations WHERE id = ?
3693
+ `);
3694
+ const self = selfStmt.all(anchorId);
3695
+ const afterStmt = db2.query(`
3696
+ SELECT id, 'observation' as type, title, text as content, project, created_at, created_at_epoch
3697
+ FROM observations
3698
+ WHERE created_at_epoch > ?
3699
+ ORDER BY created_at_epoch ASC
3700
+ LIMIT ?
3701
+ `);
3702
+ const after = afterStmt.all(anchorEpoch, depthAfter);
3703
+ return [...before, ...self, ...after];
3704
+ }
3705
+ function getProjectStats(db2, project) {
3706
+ const obsStmt = db2.query("SELECT COUNT(*) as count FROM observations WHERE project = ?");
3707
+ const sumStmt = db2.query("SELECT COUNT(*) as count FROM summaries WHERE project = ?");
3708
+ const sesStmt = db2.query("SELECT COUNT(*) as count FROM sessions WHERE project = ?");
3709
+ const prmStmt = db2.query("SELECT COUNT(*) as count FROM prompts WHERE project = ?");
3710
+ return {
3711
+ observations: obsStmt.get(project)?.count || 0,
3712
+ summaries: sumStmt.get(project)?.count || 0,
3713
+ sessions: sesStmt.get(project)?.count || 0,
3714
+ prompts: prmStmt.get(project)?.count || 0
3715
+ };
3716
+ }
3717
+
799
3718
  // src/services/worker-service.ts
800
3719
  var __worker_dirname = dirname2(fileURLToPath2(import.meta.url));
801
3720
  var PORT = process.env.KIRO_MEMORY_WORKER_PORT || process.env.CONTEXTKIT_WORKER_PORT || 3001;
802
3721
  var HOST = process.env.KIRO_MEMORY_WORKER_HOST || process.env.CONTEXTKIT_WORKER_HOST || "127.0.0.1";
803
3722
  var PID_FILE = join3(DATA_DIR, "worker.pid");
3723
+ var TOKEN_FILE = join3(DATA_DIR, "worker.token");
3724
+ var MAX_SSE_CLIENTS = 50;
804
3725
  if (!existsSync3(DATA_DIR)) {
805
3726
  mkdirSync3(DATA_DIR, { recursive: true });
806
3727
  }
3728
+ var WORKER_TOKEN = crypto.randomBytes(32).toString("hex");
3729
+ writeFileSync(TOKEN_FILE, WORKER_TOKEN, "utf-8");
3730
+ try {
3731
+ chmodSync(TOKEN_FILE, 384);
3732
+ } catch {
3733
+ }
807
3734
  var db = new KiroMemoryDatabase();
808
3735
  logger.info("WORKER", "Database initialized");
3736
+ function parseIntSafe(value, defaultVal, min, max) {
3737
+ if (!value) return defaultVal;
3738
+ const parsed = parseInt(value, 10);
3739
+ if (isNaN(parsed) || parsed < min || parsed > max) return defaultVal;
3740
+ return parsed;
3741
+ }
3742
+ function isValidProject(project) {
3743
+ return typeof project === "string" && project.length > 0 && project.length <= 200 && /^[\w\-\.\/@ ]+$/.test(project);
3744
+ }
3745
+ function isValidString(val, maxLen) {
3746
+ return typeof val === "string" && val.length <= maxLen;
3747
+ }
809
3748
  var app = express();
810
- app.use(express.json());
811
- app.use(cors());
3749
+ app.use(helmet({
3750
+ contentSecurityPolicy: {
3751
+ directives: {
3752
+ defaultSrc: ["'self'"],
3753
+ scriptSrc: ["'self'", "'unsafe-inline'"],
3754
+ styleSrc: ["'self'", "'unsafe-inline'", "https://cdn.tailwindcss.com"],
3755
+ imgSrc: ["'self'", "data:"],
3756
+ connectSrc: ["'self'"],
3757
+ fontSrc: ["'self'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"],
3758
+ frameSrc: ["'none'"]
3759
+ }
3760
+ }
3761
+ }));
3762
+ app.use(cors({
3763
+ origin: [
3764
+ `http://localhost:${PORT}`,
3765
+ `http://127.0.0.1:${PORT}`,
3766
+ `http://${HOST}:${PORT}`
3767
+ ],
3768
+ credentials: true,
3769
+ maxAge: 86400
3770
+ }));
3771
+ app.use(express.json({ limit: "1mb" }));
3772
+ app.use("/api/", rate_limit_default({
3773
+ windowMs: 6e4,
3774
+ max: 200,
3775
+ standardHeaders: true,
3776
+ legacyHeaders: false,
3777
+ message: { error: "Too many requests, retry later" }
3778
+ }));
812
3779
  var clients = [];
813
3780
  function broadcast(event, data) {
814
3781
  const message = `event: ${event}
@@ -823,14 +3790,21 @@ data: ${JSON.stringify(data)}
823
3790
  }
824
3791
  });
825
3792
  }
826
- app.post("/api/notify", express.json(), (req, res) => {
3793
+ var ALLOWED_EVENTS = /* @__PURE__ */ new Set(["observation-created", "summary-created", "prompt-created", "session-created"]);
3794
+ var notifyLimiter = rate_limit_default({ windowMs: 6e4, max: 60, standardHeaders: true, legacyHeaders: false });
3795
+ app.post("/api/notify", notifyLimiter, (req, res) => {
3796
+ const token = req.headers["x-worker-token"];
3797
+ if (token !== WORKER_TOKEN) {
3798
+ res.status(401).json({ error: "Invalid or missing X-Worker-Token" });
3799
+ return;
3800
+ }
827
3801
  const { event, data } = req.body || {};
828
- if (event && typeof event === "string") {
829
- broadcast(event, data || {});
830
- res.json({ ok: true });
831
- } else {
832
- res.status(400).json({ error: 'Campo "event" richiesto' });
3802
+ if (!event || typeof event !== "string" || !ALLOWED_EVENTS.has(event)) {
3803
+ res.status(400).json({ error: `Event must be one of: ${[...ALLOWED_EVENTS].join(", ")}` });
3804
+ return;
833
3805
  }
3806
+ broadcast(event, data || {});
3807
+ res.json({ ok: true });
834
3808
  });
835
3809
  app.get("/health", (req, res) => {
836
3810
  res.json({
@@ -840,9 +3814,14 @@ app.get("/health", (req, res) => {
840
3814
  });
841
3815
  });
842
3816
  app.get("/events", (req, res) => {
3817
+ if (clients.length >= MAX_SSE_CLIENTS) {
3818
+ res.status(503).json({ error: "Too many SSE connections" });
3819
+ return;
3820
+ }
843
3821
  res.setHeader("Content-Type", "text/event-stream");
844
3822
  res.setHeader("Cache-Control", "no-cache");
845
3823
  res.setHeader("Connection", "keep-alive");
3824
+ res.setHeader("X-Accel-Buffering", "no");
846
3825
  res.flushHeaders();
847
3826
  clients.push(res);
848
3827
  logger.info("WORKER", "SSE client connected", { clients: clients.length });
@@ -850,7 +3829,17 @@ app.get("/events", (req, res) => {
850
3829
  data: ${JSON.stringify({ timestamp: Date.now() })}
851
3830
 
852
3831
  `);
3832
+ const keepaliveInterval = setInterval(() => {
3833
+ try {
3834
+ res.write(`:keepalive ${Date.now()}
3835
+
3836
+ `);
3837
+ } catch {
3838
+ clearInterval(keepaliveInterval);
3839
+ }
3840
+ }, 15e3);
853
3841
  req.on("close", () => {
3842
+ clearInterval(keepaliveInterval);
854
3843
  const index = clients.indexOf(res);
855
3844
  if (index > -1) {
856
3845
  clients.splice(index, 1);
@@ -861,12 +3850,10 @@ data: ${JSON.stringify({ timestamp: Date.now() })}
861
3850
  app.get("/api/context/:project", (req, res) => {
862
3851
  const { project } = req.params;
863
3852
  try {
864
- const { getObservationsByProject: getObservationsByProject2 } = (init_Observations(), __toCommonJS(Observations_exports));
865
- const { getSummariesByProject: getSummariesByProject2 } = (init_Summaries(), __toCommonJS(Summaries_exports));
866
3853
  const context = {
867
3854
  project,
868
- observations: getObservationsByProject2(db.db, project, 20),
869
- summaries: getSummariesByProject2(db.db, project, 5)
3855
+ observations: getObservationsByProject(db.db, project, 20),
3856
+ summaries: getSummariesByProject(db.db, project, 5)
870
3857
  };
871
3858
  res.json(context);
872
3859
  } catch (error) {
@@ -876,9 +3863,28 @@ app.get("/api/context/:project", (req, res) => {
876
3863
  });
877
3864
  app.post("/api/observations", (req, res) => {
878
3865
  const { memorySessionId, project, type, title, content, concepts, files } = req.body;
3866
+ if (!isValidProject(project)) {
3867
+ res.status(400).json({ error: 'Invalid or missing "project"' });
3868
+ return;
3869
+ }
3870
+ if (!isValidString(title, 500)) {
3871
+ res.status(400).json({ error: 'Invalid or missing "title" (max 500 chars)' });
3872
+ return;
3873
+ }
3874
+ if (content && !isValidString(content, 1e5)) {
3875
+ res.status(400).json({ error: '"content" too large (max 100KB)' });
3876
+ return;
3877
+ }
3878
+ if (concepts && !Array.isArray(concepts)) {
3879
+ res.status(400).json({ error: '"concepts" must be an array' });
3880
+ return;
3881
+ }
3882
+ if (files && !Array.isArray(files)) {
3883
+ res.status(400).json({ error: '"files" must be an array' });
3884
+ return;
3885
+ }
879
3886
  try {
880
- const { createObservation: createObservation2 } = (init_Observations(), __toCommonJS(Observations_exports));
881
- const id = createObservation2(
3887
+ const id = createObservation(
882
3888
  db.db,
883
3889
  memorySessionId || "api-" + Date.now(),
884
3890
  project,
@@ -902,9 +3908,29 @@ app.post("/api/observations", (req, res) => {
902
3908
  });
903
3909
  app.post("/api/summaries", (req, res) => {
904
3910
  const { sessionId, project, request, learned, completed, nextSteps } = req.body;
3911
+ if (!isValidProject(project)) {
3912
+ res.status(400).json({ error: 'Invalid or missing "project"' });
3913
+ return;
3914
+ }
3915
+ const MAX_FIELD = 5e4;
3916
+ if (request && !isValidString(request, MAX_FIELD)) {
3917
+ res.status(400).json({ error: '"request" too large' });
3918
+ return;
3919
+ }
3920
+ if (learned && !isValidString(learned, MAX_FIELD)) {
3921
+ res.status(400).json({ error: '"learned" too large' });
3922
+ return;
3923
+ }
3924
+ if (completed && !isValidString(completed, MAX_FIELD)) {
3925
+ res.status(400).json({ error: '"completed" too large' });
3926
+ return;
3927
+ }
3928
+ if (nextSteps && !isValidString(nextSteps, MAX_FIELD)) {
3929
+ res.status(400).json({ error: '"nextSteps" too large' });
3930
+ return;
3931
+ }
905
3932
  try {
906
- const { createSummary: createSummary2 } = (init_Summaries(), __toCommonJS(Summaries_exports));
907
- const id = createSummary2(
3933
+ const id = createSummary(
908
3934
  db.db,
909
3935
  sessionId || "api-" + Date.now(),
910
3936
  project,
@@ -929,16 +3955,14 @@ app.get("/api/search", (req, res) => {
929
3955
  return;
930
3956
  }
931
3957
  try {
932
- const { searchObservationsFTS: searchObservationsFTS2 } = (init_Search(), __toCommonJS(Search_exports));
933
- const { searchSummariesFiltered: searchSummariesFiltered2 } = (init_Search(), __toCommonJS(Search_exports));
934
3958
  const filters = {
935
3959
  project: project || void 0,
936
3960
  type: type || void 0,
937
- limit: limit ? parseInt(limit, 10) : 20
3961
+ limit: parseIntSafe(limit, 20, 1, 100)
938
3962
  };
939
3963
  const results = {
940
- observations: searchObservationsFTS2(db.db, q, filters),
941
- summaries: searchSummariesFiltered2(db.db, q, filters)
3964
+ observations: searchObservationsFTS(db.db, q, filters),
3965
+ summaries: searchSummariesFiltered(db.db, q, filters)
942
3966
  };
943
3967
  res.json(results);
944
3968
  } catch (error) {
@@ -952,13 +3976,17 @@ app.get("/api/timeline", (req, res) => {
952
3976
  res.status(400).json({ error: 'Query parameter "anchor" is required' });
953
3977
  return;
954
3978
  }
3979
+ const anchorId = parseIntSafe(anchor, 0, 1, Number.MAX_SAFE_INTEGER);
3980
+ if (anchorId === 0) {
3981
+ res.status(400).json({ error: 'Invalid "anchor" (must be positive integer)' });
3982
+ return;
3983
+ }
955
3984
  try {
956
- const { getTimeline: getTimeline2 } = (init_Search(), __toCommonJS(Search_exports));
957
- const timeline = getTimeline2(
3985
+ const timeline = getTimeline(
958
3986
  db.db,
959
- parseInt(anchor, 10),
960
- depth_before ? parseInt(depth_before, 10) : 5,
961
- depth_after ? parseInt(depth_after, 10) : 5
3987
+ anchorId,
3988
+ parseIntSafe(depth_before, 5, 1, 50),
3989
+ parseIntSafe(depth_after, 5, 1, 50)
962
3990
  );
963
3991
  res.json({ timeline });
964
3992
  } catch (error) {
@@ -968,13 +3996,16 @@ app.get("/api/timeline", (req, res) => {
968
3996
  });
969
3997
  app.post("/api/observations/batch", (req, res) => {
970
3998
  const { ids } = req.body;
971
- if (!ids || !Array.isArray(ids)) {
972
- res.status(400).json({ error: 'Body parameter "ids" (array) is required' });
3999
+ if (!ids || !Array.isArray(ids) || ids.length === 0 || ids.length > 100) {
4000
+ res.status(400).json({ error: '"ids" must be an array of 1-100 elements' });
4001
+ return;
4002
+ }
4003
+ if (!ids.every((id) => typeof id === "number" && Number.isInteger(id) && id > 0)) {
4004
+ res.status(400).json({ error: "All IDs must be positive integers" });
973
4005
  return;
974
4006
  }
975
4007
  try {
976
- const { getObservationsByIds: getObservationsByIds2 } = (init_Search(), __toCommonJS(Search_exports));
977
- const observations = getObservationsByIds2(db.db, ids);
4008
+ const observations = getObservationsByIds(db.db, ids);
978
4009
  res.json({ observations });
979
4010
  } catch (error) {
980
4011
  logger.error("WORKER", "Batch fetch fallito", { ids }, error);
@@ -984,8 +4015,7 @@ app.post("/api/observations/batch", (req, res) => {
984
4015
  app.get("/api/stats/:project", (req, res) => {
985
4016
  const { project } = req.params;
986
4017
  try {
987
- const { getProjectStats: getProjectStats2 } = (init_Search(), __toCommonJS(Search_exports));
988
- const stats = getProjectStats2(db.db, project);
4018
+ const stats = getProjectStats(db.db, project);
989
4019
  res.json(stats);
990
4020
  } catch (error) {
991
4021
  logger.error("WORKER", "Stats fallite", { project }, error);
@@ -994,12 +4024,16 @@ app.get("/api/stats/:project", (req, res) => {
994
4024
  });
995
4025
  app.get("/api/observations", (req, res) => {
996
4026
  const { offset, limit, project } = req.query;
997
- const _offset = offset ? parseInt(offset, 10) : 0;
998
- const _limit = limit ? parseInt(limit, 10) : 50;
4027
+ const _offset = parseIntSafe(offset, 0, 0, 1e6);
4028
+ const _limit = parseIntSafe(limit, 50, 1, 200);
999
4029
  try {
4030
+ const countSql = project ? "SELECT COUNT(*) as total FROM observations WHERE project = ?" : "SELECT COUNT(*) as total FROM observations";
4031
+ const countStmt = db.db.query(countSql);
4032
+ const { total } = project ? countStmt.get(project) : countStmt.get();
1000
4033
  const sql = project ? "SELECT * FROM observations WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?" : "SELECT * FROM observations ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?";
1001
4034
  const stmt = db.db.query(sql);
1002
4035
  const rows = project ? stmt.all(project, _limit, _offset) : stmt.all(_limit, _offset);
4036
+ res.setHeader("X-Total-Count", total);
1003
4037
  res.json(rows);
1004
4038
  } catch (error) {
1005
4039
  logger.error("WORKER", "Lista osservazioni fallita", {}, error);
@@ -1008,12 +4042,16 @@ app.get("/api/observations", (req, res) => {
1008
4042
  });
1009
4043
  app.get("/api/summaries", (req, res) => {
1010
4044
  const { offset, limit, project } = req.query;
1011
- const _offset = offset ? parseInt(offset, 10) : 0;
1012
- const _limit = limit ? parseInt(limit, 10) : 20;
4045
+ const _offset = parseIntSafe(offset, 0, 0, 1e6);
4046
+ const _limit = parseIntSafe(limit, 20, 1, 200);
1013
4047
  try {
4048
+ const countSql = project ? "SELECT COUNT(*) as total FROM summaries WHERE project = ?" : "SELECT COUNT(*) as total FROM summaries";
4049
+ const countStmt = db.db.query(countSql);
4050
+ const { total } = project ? countStmt.get(project) : countStmt.get();
1014
4051
  const sql = project ? "SELECT * FROM summaries WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?" : "SELECT * FROM summaries ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?";
1015
4052
  const stmt = db.db.query(sql);
1016
4053
  const rows = project ? stmt.all(project, _limit, _offset) : stmt.all(_limit, _offset);
4054
+ res.setHeader("X-Total-Count", total);
1017
4055
  res.json(rows);
1018
4056
  } catch (error) {
1019
4057
  logger.error("WORKER", "Lista summary fallita", {}, error);
@@ -1022,12 +4060,16 @@ app.get("/api/summaries", (req, res) => {
1022
4060
  });
1023
4061
  app.get("/api/prompts", (req, res) => {
1024
4062
  const { offset, limit, project } = req.query;
1025
- const _offset = offset ? parseInt(offset, 10) : 0;
1026
- const _limit = limit ? parseInt(limit, 10) : 20;
4063
+ const _offset = parseIntSafe(offset, 0, 0, 1e6);
4064
+ const _limit = parseIntSafe(limit, 20, 1, 200);
1027
4065
  try {
4066
+ const countSql = project ? "SELECT COUNT(*) as total FROM prompts WHERE project = ?" : "SELECT COUNT(*) as total FROM prompts";
4067
+ const countStmt = db.db.query(countSql);
4068
+ const { total } = project ? countStmt.get(project) : countStmt.get();
1028
4069
  const sql = project ? "SELECT * FROM prompts WHERE project = ? ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?" : "SELECT * FROM prompts ORDER BY created_at_epoch DESC LIMIT ? OFFSET ?";
1029
4070
  const stmt = db.db.query(sql);
1030
4071
  const rows = project ? stmt.all(project, _limit, _offset) : stmt.all(_limit, _offset);
4072
+ res.setHeader("X-Total-Count", total);
1031
4073
  res.json(rows);
1032
4074
  } catch (error) {
1033
4075
  logger.error("WORKER", "Lista prompt fallita", {}, error);