@lousy-agents/cli 5.21.2 → 5.21.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/copilot-with-fastify/.devcontainer/devcontainer.json +2 -2
- package/api/copilot-with-fastify/.nvmrc +1 -1
- package/api/copilot-with-fastify/biome.template.json +1 -1
- package/api/copilot-with-fastify/package-lock.json +436 -473
- package/api/copilot-with-fastify/package.json +8 -8
- package/cli/copilot-with-citty/.devcontainer/devcontainer.json +2 -2
- package/cli/copilot-with-citty/.nvmrc +1 -1
- package/cli/copilot-with-citty/biome.template.json +1 -1
- package/cli/copilot-with-citty/package.json +6 -6
- package/dist/475.js +110 -49
- package/dist/61.js +281 -0
- package/dist/962.js +10 -9
- package/dist/index.js +52133 -23075
- package/package.json +2 -2
- package/ui/copilot-with-react/.devcontainer/devcontainer.json +2 -2
- package/ui/copilot-with-react/.nvmrc +1 -1
- package/ui/copilot-with-react/biome.template.json +1 -1
- package/ui/copilot-with-react/package.json +14 -14
- package/dist/164.js +0 -125
|
@@ -18,25 +18,25 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@fastify/cors": "11.3.0",
|
|
20
20
|
"@fastify/sensible": "6.0.5",
|
|
21
|
-
"fastify": "5.
|
|
21
|
+
"fastify": "5.12.1",
|
|
22
22
|
"kysely": "0.29.5",
|
|
23
23
|
"pino": "10.3.1",
|
|
24
24
|
"postgres": "3.4.9",
|
|
25
|
-
"zod": "4.
|
|
25
|
+
"zod": "4.5.2"
|
|
26
26
|
},
|
|
27
27
|
"overrides": {
|
|
28
|
-
"zod": "4.
|
|
28
|
+
"zod": "4.5.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@biomejs/biome": "2.5.
|
|
32
|
-
"@lousy-agents/mcp": "5.21.
|
|
33
|
-
"@modelcontextprotocol/server-sequential-thinking": "2026.
|
|
31
|
+
"@biomejs/biome": "2.5.11",
|
|
32
|
+
"@lousy-agents/mcp": "5.21.3",
|
|
33
|
+
"@modelcontextprotocol/server-sequential-thinking": "2026.8.31",
|
|
34
34
|
"@testcontainers/postgresql": "12.1.0",
|
|
35
35
|
"@types/node": "24.13.3",
|
|
36
36
|
"chance": "1.1.13",
|
|
37
37
|
"testcontainers": "12.1.0",
|
|
38
|
-
"tsx": "4.23.
|
|
38
|
+
"tsx": "4.23.13",
|
|
39
39
|
"typescript": "7.0.2",
|
|
40
|
-
"vitest": "4.1.
|
|
40
|
+
"vitest": "4.1.11"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04@sha256:4bcb1b466771b1ba1ea110e2a27daea2f6093f9527fb75ee59703ec89b5561cb",
|
|
4
4
|
"features": {
|
|
5
5
|
"ghcr.io/devcontainers/features/node:1.7.1": {
|
|
6
|
-
"version": "24.
|
|
6
|
+
"version": "24.20.0"
|
|
7
7
|
},
|
|
8
8
|
"ghcr.io/devcontainers/features/github-cli:1.1.0": {
|
|
9
9
|
"version": "latest"
|
|
10
10
|
},
|
|
11
11
|
"ghcr.io/devcontainers/features/copilot-cli:1.0.0": {},
|
|
12
12
|
"ghcr.io/anthropics/devcontainer-features/claude-code:1": {
|
|
13
|
-
"version": "v2.1.
|
|
13
|
+
"version": "v2.1.252"
|
|
14
14
|
},
|
|
15
15
|
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1.0.2": {
|
|
16
16
|
"packages": "yamllint, shellcheck",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
24.
|
|
1
|
+
24.20.0
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"citty": "0.2.2",
|
|
18
18
|
"consola": "3.4.2",
|
|
19
|
-
"zod": "4.
|
|
19
|
+
"zod": "4.5.2"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@biomejs/biome": "2.5.
|
|
23
|
-
"@lousy-agents/mcp": "5.21.
|
|
24
|
-
"@modelcontextprotocol/server-sequential-thinking": "2026.
|
|
22
|
+
"@biomejs/biome": "2.5.11",
|
|
23
|
+
"@lousy-agents/mcp": "5.21.3",
|
|
24
|
+
"@modelcontextprotocol/server-sequential-thinking": "2026.8.31",
|
|
25
25
|
"@types/node": "24.13.3",
|
|
26
26
|
"chance": "1.1.13",
|
|
27
|
-
"tsx": "4.23.
|
|
27
|
+
"tsx": "4.23.13",
|
|
28
28
|
"typescript": "7.0.2",
|
|
29
|
-
"vitest": "4.1.
|
|
29
|
+
"vitest": "4.1.11"
|
|
30
30
|
}
|
|
31
31
|
}
|
package/dist/475.js
CHANGED
|
@@ -41,7 +41,9 @@ const defaultOptions = {
|
|
|
41
41
|
lstat: false,
|
|
42
42
|
depth: 2147483648,
|
|
43
43
|
alwaysStat: false,
|
|
44
|
-
|
|
44
|
+
// Throughput is flat from 16 to 65536 (traversal is I/O-bound), but
|
|
45
|
+
// batches of 1024+ entries survive young-gen GC and bloat RSS ~20-60%.
|
|
46
|
+
highWaterMark: 256,
|
|
45
47
|
};
|
|
46
48
|
Object.freeze(defaultOptions);
|
|
47
49
|
const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';
|
|
@@ -80,8 +82,12 @@ const normalizeFilter = (filter) => {
|
|
|
80
82
|
}
|
|
81
83
|
return emptyFn;
|
|
82
84
|
};
|
|
83
|
-
/** Readable readdir stream, emitting new files as they're being listed. */
|
|
84
85
|
class ReaddirpStream extends external_node_stream_.Readable {
|
|
86
|
+
/**
|
|
87
|
+
* Directories discovered but not yet emitted from. Listings are read
|
|
88
|
+
* lazily (on pop, plus one prefetch) instead of eagerly on discovery:
|
|
89
|
+
* keeping whole listings for every queued dir balloons RAM on wide trees.
|
|
90
|
+
*/
|
|
85
91
|
parents;
|
|
86
92
|
reading;
|
|
87
93
|
parent;
|
|
@@ -96,14 +102,17 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
96
102
|
_rdOptions;
|
|
97
103
|
_fileFilter;
|
|
98
104
|
_directoryFilter;
|
|
105
|
+
_relStart;
|
|
99
106
|
constructor(options = {}) {
|
|
100
107
|
super({
|
|
101
108
|
objectMode: true,
|
|
102
109
|
autoDestroy: true,
|
|
103
|
-
highWaterMark: options.highWaterMark,
|
|
110
|
+
highWaterMark: options.highWaterMark ?? defaultOptions.highWaterMark,
|
|
104
111
|
});
|
|
105
112
|
const opts = { ...defaultOptions, ...options };
|
|
106
|
-
|
|
113
|
+
// Use ?? so an explicit `undefined` in user options doesn't shadow defaults.
|
|
114
|
+
const root = opts.root ?? defaultOptions.root;
|
|
115
|
+
const type = opts.type ?? defaultOptions.type;
|
|
107
116
|
this._fileFilter = normalizeFilter(opts.fileFilter);
|
|
108
117
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
109
118
|
const statMethod = opts.lstat ? promises_.lstat : promises_.stat;
|
|
@@ -116,15 +125,23 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
116
125
|
}
|
|
117
126
|
this._maxDepth =
|
|
118
127
|
opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
|
|
119
|
-
this._wantsDir =
|
|
120
|
-
this._wantsFile =
|
|
128
|
+
this._wantsDir = DIR_TYPES.has(type);
|
|
129
|
+
this._wantsFile = FILE_TYPES.has(type);
|
|
121
130
|
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
122
131
|
this._root = (0,external_node_path_.resolve)(root);
|
|
132
|
+
// Every fullPath is `_root + sep + relative path` (see _formatEntry), so
|
|
133
|
+
// the relative path is a slice starting past the root and its trailing
|
|
134
|
+
// separator (which resolved paths lack, except fs roots like '/', 'C:\').
|
|
135
|
+
this._relStart = this._root.endsWith(external_node_path_.sep) ? this._root.length : this._root.length + 1;
|
|
123
136
|
this._isDirent = !opts.alwaysStat;
|
|
124
137
|
this._statsProp = this._isDirent ? 'dirent' : 'stats';
|
|
125
138
|
this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };
|
|
126
|
-
// Launch stream with one parent, the root dir
|
|
127
|
-
|
|
139
|
+
// Launch stream with one parent, the root dir, whose readdir starts
|
|
140
|
+
// right away. Explore the resolved root so all parent paths stay
|
|
141
|
+
// absolute even if process.cwd() changes mid-iteration.
|
|
142
|
+
const rootDir = { path: this._root, depth: 1 };
|
|
143
|
+
rootDir.pending = this._exploreDir(this._root, 1);
|
|
144
|
+
this.parents = [rootDir];
|
|
128
145
|
this.reading = false;
|
|
129
146
|
this.parent = undefined;
|
|
130
147
|
}
|
|
@@ -139,16 +156,25 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
139
156
|
if (fil && fil.length > 0) {
|
|
140
157
|
const { path, depth } = par;
|
|
141
158
|
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));
|
|
142
|
-
|
|
159
|
+
// In dirent mode _formatEntry is synchronous: skip Promise.all and
|
|
160
|
+
// its per-entry microtask overhead.
|
|
161
|
+
const awaited = this._isDirent
|
|
162
|
+
? slice
|
|
163
|
+
: await Promise.all(slice);
|
|
143
164
|
for (const entry of awaited) {
|
|
144
165
|
if (!entry)
|
|
145
166
|
continue;
|
|
146
167
|
if (this.destroyed)
|
|
147
168
|
return;
|
|
148
|
-
|
|
169
|
+
// Only symlinks require async work; plain files / dirs resolve synchronously.
|
|
170
|
+
let entryType = this._getEntryType(entry);
|
|
171
|
+
if (typeof entryType !== 'string')
|
|
172
|
+
entryType = await entryType;
|
|
149
173
|
if (entryType === 'directory' && this._directoryFilter(entry)) {
|
|
150
174
|
if (depth <= this._maxDepth) {
|
|
151
|
-
this.
|
|
175
|
+
// Lazy: don't readdir until this dir is popped. Keeping whole
|
|
176
|
+
// listings for every queued dir would balloon RAM on wide trees.
|
|
177
|
+
this.parents.push({ path: entry.fullPath, depth: depth + 1 });
|
|
152
178
|
}
|
|
153
179
|
if (this._wantsDir) {
|
|
154
180
|
this.push(entry);
|
|
@@ -170,7 +196,15 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
170
196
|
this.push(null);
|
|
171
197
|
break;
|
|
172
198
|
}
|
|
173
|
-
|
|
199
|
+
const dir = parent.pending ?? this._exploreDir(parent.path, parent.depth);
|
|
200
|
+
// Prefetch the next dir so its readdir overlaps with processing
|
|
201
|
+
// this one's entries. Only the stack top is prefetched, keeping at
|
|
202
|
+
// most a handful of listings (~tree depth) in RAM at once.
|
|
203
|
+
const next = this.parents[this.parents.length - 1];
|
|
204
|
+
if (next && !next.pending) {
|
|
205
|
+
next.pending = this._exploreDir(next.path, next.depth);
|
|
206
|
+
}
|
|
207
|
+
this.parent = await dir;
|
|
174
208
|
if (this.destroyed)
|
|
175
209
|
return;
|
|
176
210
|
}
|
|
@@ -183,6 +217,18 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
183
217
|
this.reading = false;
|
|
184
218
|
}
|
|
185
219
|
}
|
|
220
|
+
// NOTE: native `readdir(path, { recursive: true })` was evaluated as a
|
|
221
|
+
// replacement for this per-directory traversal and rejected:
|
|
222
|
+
// - Not faster: node implements it in JS, walking directories sequentially
|
|
223
|
+
// just like this loop, but with extra path bookkeeping. Benchmarks
|
|
224
|
+
// (node 24): ~10% slower on wide trees, ~40% slower on small ones,
|
|
225
|
+
// parity on deep ones.
|
|
226
|
+
// - Much more RAM: it buffers the entire subtree listing in one array,
|
|
227
|
+
// instead of one directory at a time, defeating streaming.
|
|
228
|
+
// - Semantics diverge: it can't limit depth, can't skip directories a
|
|
229
|
+
// directoryFilter rejects, doesn't follow symlinked dirs, and fails
|
|
230
|
+
// wholesale (all entries lost) if anything in the subtree is unreadable,
|
|
231
|
+
// instead of emitting a 'warn' and continuing.
|
|
186
232
|
async _exploreDir(path, depth) {
|
|
187
233
|
let files;
|
|
188
234
|
try {
|
|
@@ -193,19 +239,27 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
193
239
|
}
|
|
194
240
|
return { files, depth, path };
|
|
195
241
|
}
|
|
196
|
-
|
|
197
|
-
|
|
242
|
+
// Synchronous in dirent mode; returns a promise only when stats are needed.
|
|
243
|
+
_formatEntry(dirent, path) {
|
|
198
244
|
const basename = this._isDirent ? dirent.name : dirent;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
245
|
+
// `path` is always an absolute, normalized parent dir (see _exploreDir
|
|
246
|
+
// seeding in the constructor), so a plain join is enough — resolve()
|
|
247
|
+
// would re-read cwd on every entry.
|
|
248
|
+
const fullPath = (0,external_node_path_.join)(path, basename);
|
|
249
|
+
// Slice instead of path.relative(): equivalent here (fullPath is always
|
|
250
|
+
// under _root) and avoids several intermediate allocations per entry.
|
|
251
|
+
const entry = { path: fullPath.slice(this._relStart), fullPath, basename };
|
|
252
|
+
if (this._isDirent) {
|
|
253
|
+
entry.dirent = dirent;
|
|
254
|
+
return entry;
|
|
203
255
|
}
|
|
204
|
-
|
|
256
|
+
return this._stat(fullPath).then((stats) => {
|
|
257
|
+
entry.stats = stats;
|
|
258
|
+
return entry;
|
|
259
|
+
}, (err) => {
|
|
205
260
|
this._onError(err);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
return entry;
|
|
261
|
+
return undefined;
|
|
262
|
+
});
|
|
209
263
|
}
|
|
210
264
|
_onError(err) {
|
|
211
265
|
if (isNormalFlowError(err) && !this.destroyed) {
|
|
@@ -215,10 +269,12 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
215
269
|
this.destroy(err);
|
|
216
270
|
}
|
|
217
271
|
}
|
|
218
|
-
|
|
272
|
+
// Synchronous for regular files and directories; returns a promise only for
|
|
273
|
+
// symlinks, which need realpath() to be classified.
|
|
274
|
+
_getEntryType(entry) {
|
|
219
275
|
// entry may be undefined, because a warning or an error were emitted
|
|
220
276
|
// and the statsProp is undefined
|
|
221
|
-
if (!entry
|
|
277
|
+
if (!entry || !(this._statsProp in entry)) {
|
|
222
278
|
return '';
|
|
223
279
|
}
|
|
224
280
|
const stats = entry[this._statsProp];
|
|
@@ -226,30 +282,34 @@ class ReaddirpStream extends external_node_stream_.Readable {
|
|
|
226
282
|
return 'file';
|
|
227
283
|
if (stats.isDirectory())
|
|
228
284
|
return 'directory';
|
|
229
|
-
if (stats
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
|
|
241
|
-
// @ts-ignore
|
|
242
|
-
recursiveError.code = RECURSIVE_ERROR_CODE;
|
|
243
|
-
return this._onError(recursiveError);
|
|
244
|
-
}
|
|
245
|
-
return 'directory';
|
|
246
|
-
}
|
|
285
|
+
if (stats.isSymbolicLink())
|
|
286
|
+
return this._getSymlinkEntryType(entry);
|
|
287
|
+
return '';
|
|
288
|
+
}
|
|
289
|
+
async _getSymlinkEntryType(entry) {
|
|
290
|
+
const full = entry.fullPath;
|
|
291
|
+
try {
|
|
292
|
+
const entryRealPath = await (0,promises_.realpath)(full);
|
|
293
|
+
const entryRealPathStats = await (0,promises_.lstat)(entryRealPath);
|
|
294
|
+
if (entryRealPathStats.isFile()) {
|
|
295
|
+
return 'file';
|
|
247
296
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
297
|
+
if (entryRealPathStats.isDirectory()) {
|
|
298
|
+
const len = entryRealPath.length;
|
|
299
|
+
if (full.startsWith(entryRealPath) && full[len] === external_node_path_.sep) {
|
|
300
|
+
const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
|
|
301
|
+
// @ts-ignore
|
|
302
|
+
recursiveError.code = RECURSIVE_ERROR_CODE;
|
|
303
|
+
this._onError(recursiveError);
|
|
304
|
+
return '';
|
|
305
|
+
}
|
|
306
|
+
return 'directory';
|
|
251
307
|
}
|
|
252
308
|
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
this._onError(error);
|
|
311
|
+
}
|
|
312
|
+
return '';
|
|
253
313
|
}
|
|
254
314
|
_includeAsFile(entry) {
|
|
255
315
|
const stats = entry && entry[this._statsProp];
|
|
@@ -267,8 +327,6 @@ function readdirp(root, options = {}) {
|
|
|
267
327
|
let type = options.entryType || options.type;
|
|
268
328
|
if (type === 'both')
|
|
269
329
|
type = EntryTypes.FILE_DIR_TYPE; // backwards-compatibility
|
|
270
|
-
if (type)
|
|
271
|
-
options.type = type;
|
|
272
330
|
if (!root) {
|
|
273
331
|
throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');
|
|
274
332
|
}
|
|
@@ -278,8 +336,11 @@ function readdirp(root, options = {}) {
|
|
|
278
336
|
else if (type && !ALL_TYPES.includes(type)) {
|
|
279
337
|
throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);
|
|
280
338
|
}
|
|
281
|
-
options
|
|
282
|
-
|
|
339
|
+
// Copy options instead of mutating the caller's object.
|
|
340
|
+
const opts = { ...options, root };
|
|
341
|
+
if (type)
|
|
342
|
+
opts.type = type;
|
|
343
|
+
return new ReaddirpStream(opts);
|
|
283
344
|
}
|
|
284
345
|
/**
|
|
285
346
|
* Promise version: Reads all files and directories in given root recursively.
|
package/dist/61.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
export const __rspack_esm_id = 61;
|
|
2
|
+
export const __rspack_esm_ids = [61];
|
|
3
|
+
export const __webpack_modules__ = {
|
|
4
|
+
2244(__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
5
|
+
|
|
6
|
+
// EXPORTS
|
|
7
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
8
|
+
diff: () => (/* binding */ diff)
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// UNUSED EXPORTS: isEqual
|
|
12
|
+
|
|
13
|
+
;// CONCATENATED MODULE: ../../node_modules/ohash/dist/_chunks/is-equal.mjs
|
|
14
|
+
function serialize(input) {
|
|
15
|
+
if (typeof input === "string") return `'${input}'`;
|
|
16
|
+
return new is_equal_Serializer().serialize(input);
|
|
17
|
+
}
|
|
18
|
+
const asciiOrder = " _-,;:!?.'\"()[]{}@*/\\&#%`^+<=>|~$0123456789abcdefghijklmnopqrstuvwxyz";
|
|
19
|
+
const asciiWeights = /*@__PURE__*/ (function() {
|
|
20
|
+
const weights = /* @__PURE__ */ new Uint8Array(128);
|
|
21
|
+
for (let i = 0; i < 69; i++) weights[asciiOrder.charCodeAt(i)] = i + 1;
|
|
22
|
+
for (let code = 65; code <= 90; code++) weights[code] = weights[code + 32];
|
|
23
|
+
return weights;
|
|
24
|
+
})();
|
|
25
|
+
function compareStrings(a, b) {
|
|
26
|
+
if (a === b) return 0;
|
|
27
|
+
const length = Math.min(a.length, b.length);
|
|
28
|
+
let tieBreaker = 0;
|
|
29
|
+
for (let i = 0; i < length; i++) {
|
|
30
|
+
const codeA = a.charCodeAt(i);
|
|
31
|
+
const codeB = b.charCodeAt(i);
|
|
32
|
+
if (codeA === codeB) continue;
|
|
33
|
+
const weightA = codeA < 128 && asciiWeights[codeA] ? asciiWeights[codeA] : codeA + 128;
|
|
34
|
+
const weightB = codeB < 128 && asciiWeights[codeB] ? asciiWeights[codeB] : codeB + 128;
|
|
35
|
+
if (weightA !== weightB) return weightA < weightB ? -1 : 1;
|
|
36
|
+
if (tieBreaker === 0) tieBreaker = codeA > codeB ? -1 : 1;
|
|
37
|
+
}
|
|
38
|
+
if (a.length !== b.length) return a.length < b.length ? -1 : 1;
|
|
39
|
+
return tieBreaker;
|
|
40
|
+
}
|
|
41
|
+
const is_equal_Serializer = /*@__PURE__*/ (function() {
|
|
42
|
+
class Serializer {
|
|
43
|
+
#context = /* @__PURE__ */ new Map();
|
|
44
|
+
compare(a, b) {
|
|
45
|
+
const typeA = typeof a;
|
|
46
|
+
const typeB = typeof b;
|
|
47
|
+
if (typeA === "string" && typeB === "string") return compareStrings(a, b);
|
|
48
|
+
if (typeA === "number" && typeB === "number") return a - b;
|
|
49
|
+
return compareStrings(this.serialize(a, true), this.serialize(b, true));
|
|
50
|
+
}
|
|
51
|
+
serialize(value, noQuotes) {
|
|
52
|
+
if (value === null) return "null";
|
|
53
|
+
switch (typeof value) {
|
|
54
|
+
case "string": return noQuotes ? value : `'${value}'`;
|
|
55
|
+
case "bigint": return `${value}n`;
|
|
56
|
+
case "object": return this.$object(value);
|
|
57
|
+
case "function": return this.$function(value);
|
|
58
|
+
}
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
61
|
+
serializeObject(object) {
|
|
62
|
+
const objString = Object.prototype.toString.call(object);
|
|
63
|
+
if (objString !== "[object Object]") return this.serializeBuiltInType(objString.length < 10 ? `unknown:${objString}` : objString.slice(8, -1), object);
|
|
64
|
+
const constructor = object.constructor;
|
|
65
|
+
const objName = constructor === Object || constructor === void 0 ? "" : constructor.name;
|
|
66
|
+
if (objName !== "" && globalThis[objName] === constructor) return this.serializeBuiltInType(objName, object);
|
|
67
|
+
if ("toJSON" in object && typeof object.toJSON === "function") {
|
|
68
|
+
const json = object.toJSON();
|
|
69
|
+
return objName + (json !== null && typeof json === "object" ? this.$object(json) : `(${this.serialize(json)})`);
|
|
70
|
+
}
|
|
71
|
+
const keys = Object.keys(object).sort(compareStrings);
|
|
72
|
+
let content = `${objName}{`;
|
|
73
|
+
for (let i = 0; i < keys.length; i++) {
|
|
74
|
+
const key = keys[i];
|
|
75
|
+
content += `${key}:${this.serialize(object[key])}`;
|
|
76
|
+
if (i < keys.length - 1) content += ",";
|
|
77
|
+
}
|
|
78
|
+
return content + "}";
|
|
79
|
+
}
|
|
80
|
+
serializeBuiltInType(type, object) {
|
|
81
|
+
const handler = this["$" + type];
|
|
82
|
+
if (handler) return handler.call(this, object);
|
|
83
|
+
if (typeof object.entries === "function") return this.serializeObjectEntries(type, object.entries());
|
|
84
|
+
throw new Error(`Cannot serialize ${type}`);
|
|
85
|
+
}
|
|
86
|
+
serializeObjectEntries(type, entries) {
|
|
87
|
+
const sortedEntries = Array.from(entries).sort((a, b) => this.compare(a[0], b[0]));
|
|
88
|
+
let content = `${type}{`;
|
|
89
|
+
for (let i = 0; i < sortedEntries.length; i++) {
|
|
90
|
+
const [key, value] = sortedEntries[i];
|
|
91
|
+
content += `${this.serialize(key, true)}:${this.serialize(value)}`;
|
|
92
|
+
if (i < sortedEntries.length - 1) content += ",";
|
|
93
|
+
}
|
|
94
|
+
return content + "}";
|
|
95
|
+
}
|
|
96
|
+
$object(object) {
|
|
97
|
+
let content = this.#context.get(object);
|
|
98
|
+
if (content === void 0) {
|
|
99
|
+
this.#context.set(object, `#${this.#context.size}`);
|
|
100
|
+
content = this.serializeObject(object);
|
|
101
|
+
this.#context.set(object, content);
|
|
102
|
+
}
|
|
103
|
+
return content;
|
|
104
|
+
}
|
|
105
|
+
$function(fn) {
|
|
106
|
+
const fnStr = Function.prototype.toString.call(fn);
|
|
107
|
+
if (fnStr.slice(-15) === "[native code] }") return `${fn.name || ""}()[native]`;
|
|
108
|
+
return `${fn.name}(${fn.length})${fnStr.replace(/\s*\n\s*/g, "")}`;
|
|
109
|
+
}
|
|
110
|
+
$Array(arr) {
|
|
111
|
+
let content = "[";
|
|
112
|
+
for (let i = 0; i < arr.length; i++) {
|
|
113
|
+
content += this.serialize(arr[i]);
|
|
114
|
+
if (i < arr.length - 1) content += ",";
|
|
115
|
+
}
|
|
116
|
+
return content + "]";
|
|
117
|
+
}
|
|
118
|
+
$Date(date) {
|
|
119
|
+
try {
|
|
120
|
+
return `Date(${date.toISOString()})`;
|
|
121
|
+
} catch {
|
|
122
|
+
return `Date(null)`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
$ArrayBuffer(arr) {
|
|
126
|
+
return `ArrayBuffer[${new Uint8Array(arr).join(",")}]`;
|
|
127
|
+
}
|
|
128
|
+
$Set(set) {
|
|
129
|
+
return `Set${this.$Array(Array.from(set).sort((a, b) => this.compare(a, b)))}`;
|
|
130
|
+
}
|
|
131
|
+
$Map(map) {
|
|
132
|
+
return this.serializeObjectEntries("Map", map.entries());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (const type of [
|
|
136
|
+
"Error",
|
|
137
|
+
"RegExp",
|
|
138
|
+
"URL"
|
|
139
|
+
]) Serializer.prototype["$" + type] = function(val) {
|
|
140
|
+
return `${type}(${val})`;
|
|
141
|
+
};
|
|
142
|
+
for (const type of [
|
|
143
|
+
"Int8Array",
|
|
144
|
+
"Uint8Array",
|
|
145
|
+
"Uint8ClampedArray",
|
|
146
|
+
"Int16Array",
|
|
147
|
+
"Uint16Array",
|
|
148
|
+
"Int32Array",
|
|
149
|
+
"Uint32Array",
|
|
150
|
+
"Float32Array",
|
|
151
|
+
"Float64Array"
|
|
152
|
+
]) Serializer.prototype["$" + type] = function(arr) {
|
|
153
|
+
return `${type}[${arr.join(",")}]`;
|
|
154
|
+
};
|
|
155
|
+
for (const type of ["BigInt64Array", "BigUint64Array"]) Serializer.prototype["$" + type] = function(arr) {
|
|
156
|
+
return `${type}[${arr.join("n,")}${arr.length > 0 ? "n" : ""}]`;
|
|
157
|
+
};
|
|
158
|
+
return Serializer;
|
|
159
|
+
})();
|
|
160
|
+
function isEqual(object1, object2) {
|
|
161
|
+
if (object1 === object2) return true;
|
|
162
|
+
if (serialize(object1) === serialize(object2)) return true;
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
;// CONCATENATED MODULE: ../../node_modules/ohash/dist/utils/index.mjs
|
|
168
|
+
|
|
169
|
+
const PROTO_KEY = "__proto__";
|
|
170
|
+
function diff(obj1, obj2) {
|
|
171
|
+
const diffs = [];
|
|
172
|
+
_diff(obj1, obj2, "", diffs);
|
|
173
|
+
return diffs;
|
|
174
|
+
}
|
|
175
|
+
function _diff(v1, v2, key, out) {
|
|
176
|
+
if (v1 === v2) return;
|
|
177
|
+
const leaf1 = v1 === null || typeof v1 !== "object";
|
|
178
|
+
const leaf2 = v2 === null || typeof v2 !== "object";
|
|
179
|
+
if (!leaf1 && !leaf2) {
|
|
180
|
+
const entries1 = _entries(v1);
|
|
181
|
+
const entries2 = _entries(v2);
|
|
182
|
+
for (const [k, child1] of entries1) {
|
|
183
|
+
const childKey = key ? `${key}.${k}` : k;
|
|
184
|
+
if (entries2.has(k)) _diff(child1, entries2.get(k), childKey, out);
|
|
185
|
+
else out.push(new DiffEntry(childKey, "removed", void 0, _toHashedObject(child1, childKey)));
|
|
186
|
+
}
|
|
187
|
+
for (const [k, child2] of entries2) {
|
|
188
|
+
if (entries1.has(k)) continue;
|
|
189
|
+
const childKey = key ? `${key}.${k}` : k;
|
|
190
|
+
out.push(new DiffEntry(childKey, "added", _toHashedObject(child2, childKey)));
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (_hasKeys(v1, leaf1) || _hasKeys(v2, leaf2)) return;
|
|
195
|
+
const node1 = _emptyOrLeafNode(v1, key, leaf1);
|
|
196
|
+
const node2 = _emptyOrLeafNode(v2, key, leaf2);
|
|
197
|
+
if (node1.hash !== node2.hash) out.push(new DiffEntry(key, "changed", node2, node1));
|
|
198
|
+
}
|
|
199
|
+
function _entries(value) {
|
|
200
|
+
const entries = /* @__PURE__ */ new Map();
|
|
201
|
+
for (const key in value) if (key !== PROTO_KEY) entries.set(key, value[key]);
|
|
202
|
+
return entries;
|
|
203
|
+
}
|
|
204
|
+
function _hasKeys(value, isLeaf) {
|
|
205
|
+
if (isLeaf) return false;
|
|
206
|
+
for (const key in value) if (key !== PROTO_KEY) return true;
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
function _emptyOrLeafNode(value, key, isLeaf) {
|
|
210
|
+
return isLeaf ? new DiffHashedObject(key, value, _leafHash(value)) : new DiffHashedObject(key, value, "{}", Object.create(null));
|
|
211
|
+
}
|
|
212
|
+
function _toHashedObject(obj, key = "") {
|
|
213
|
+
if (obj === null || typeof obj !== "object") return new DiffHashedObject(key, obj, _leafHash(obj));
|
|
214
|
+
const props = Object.create(null);
|
|
215
|
+
const hashes = [];
|
|
216
|
+
for (const _key in obj) {
|
|
217
|
+
if (_key === PROTO_KEY) continue;
|
|
218
|
+
const child = _toHashedObject(obj[_key], key ? `${key}.${_key}` : _key);
|
|
219
|
+
props[_key] = child;
|
|
220
|
+
hashes.push(child.hash);
|
|
221
|
+
}
|
|
222
|
+
return new DiffHashedObject(key, obj, `{${hashes.join(":")}}`, props);
|
|
223
|
+
}
|
|
224
|
+
function _leafHash(value) {
|
|
225
|
+
switch (typeof value) {
|
|
226
|
+
case "string": return `'${value}'`;
|
|
227
|
+
case "number":
|
|
228
|
+
case "boolean": return "" + value;
|
|
229
|
+
case "bigint": return `${value}n`;
|
|
230
|
+
default: return serialize(value);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
var DiffEntry = class {
|
|
234
|
+
key;
|
|
235
|
+
type;
|
|
236
|
+
newValue;
|
|
237
|
+
oldValue;
|
|
238
|
+
constructor(key, type, newValue, oldValue) {
|
|
239
|
+
this.key = key;
|
|
240
|
+
this.type = type;
|
|
241
|
+
this.newValue = newValue;
|
|
242
|
+
this.oldValue = oldValue;
|
|
243
|
+
}
|
|
244
|
+
toString() {
|
|
245
|
+
return this.toJSON();
|
|
246
|
+
}
|
|
247
|
+
toJSON() {
|
|
248
|
+
switch (this.type) {
|
|
249
|
+
case "added": return `Added \`${this.key}\``;
|
|
250
|
+
case "removed": return `Removed \`${this.key}\``;
|
|
251
|
+
case "changed": return `Changed \`${this.key}\` from \`${this.oldValue?.toString() || "-"}\` to \`${this.newValue?.toString()}\``;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var DiffHashedObject = class {
|
|
256
|
+
key;
|
|
257
|
+
value;
|
|
258
|
+
hash;
|
|
259
|
+
props;
|
|
260
|
+
constructor(key, value, hash, props) {
|
|
261
|
+
this.key = key;
|
|
262
|
+
this.value = value;
|
|
263
|
+
this.hash = hash;
|
|
264
|
+
this.props = props;
|
|
265
|
+
}
|
|
266
|
+
toString() {
|
|
267
|
+
if (this.props) return `{${Object.keys(this.props).join(",")}}`;
|
|
268
|
+
else return JSON.stringify(this.value);
|
|
269
|
+
}
|
|
270
|
+
toJSON() {
|
|
271
|
+
const k = this.key || ".";
|
|
272
|
+
if (this.props) return `${k}({${Object.keys(this.props).join(",")}})`;
|
|
273
|
+
return `${k}(${this.value})`;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
};
|