colonynote 1.0.0-beta.10 → 1.0.0-beta.13
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/bin/colonynote.js +40 -78
- package/dist/client/assets/_baseUniq-9Pn_qMNx.js +1 -0
- package/dist/client/assets/{arc-CS-lnk5Q.js → arc-CwgOuPiQ.js} +1 -1
- package/dist/client/assets/architectureDiagram-Q4EWVU46-DH8zBsFD.js +36 -0
- package/dist/client/assets/{blockDiagram-WCTKOSBZ-B1FE-gTT.js → blockDiagram-DXYQGD6D-Rinexrq-.js} +6 -6
- package/dist/client/assets/c4Diagram-AHTNJAMY-DUJE4tY8.js +10 -0
- package/dist/client/assets/channel-BJqbnDhj.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-C9NhnJ5n.js → chunk-4BX2VUAB-CVubEDEM.js} +1 -1
- package/dist/client/assets/chunk-4TB4RGXK-GtceFh5R.js +206 -0
- package/dist/client/assets/{chunk-55IACEB6-CG_ik_rT.js → chunk-55IACEB6-BvQuQjO3.js} +1 -1
- package/dist/client/assets/{chunk-KX2RTZJC-DeSMkxPM.js → chunk-EDXVE4YY-DwTEXUMb.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-CGeJa0Td.js → chunk-FMBD7UC4-CP_VCEId.js} +1 -1
- package/dist/client/assets/chunk-OYMX7WX6-DekTZ61_.js +231 -0
- package/dist/client/assets/{chunk-QZHKN3VN-DyrAfKbs.js → chunk-QZHKN3VN-DaQazFvi.js} +1 -1
- package/dist/client/assets/{chunk-JSJVCQXG-wTqBaIzj.js → chunk-YZCP3GAM-DkNfaXLf.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DvENVsHG.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-DvENVsHG.js +1 -0
- package/dist/client/assets/clone-Ge_jPAPG.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-DXvrsg79.js → cose-bilkent-S5V4N54A-Db3Okii7.js} +1 -1
- package/dist/client/assets/{cytoscape.esm-BQaXIfA_.js → cytoscape.esm-DxGcaOPV.js} +4 -4
- package/dist/client/assets/dagre-KV5264BT-CWRMd3SH.js +4 -0
- package/dist/client/assets/diagram-5BDNPKRD-8TiBcRv9.js +10 -0
- package/dist/client/assets/diagram-G4DWMVQ6-Djb_aFKY.js +24 -0
- package/dist/client/assets/diagram-MMDJMWI5-DhCslint.js +43 -0
- package/dist/client/assets/diagram-TYMM5635-DhePsl7z.js +24 -0
- package/dist/client/assets/erDiagram-SMLLAGMA-COaIktX_.js +85 -0
- package/dist/client/assets/flowDiagram-DWJPFMVM-BiDAMluS.js +162 -0
- package/dist/client/assets/{ganttDiagram-A5KZAMGK-D5fb9XZy.js → ganttDiagram-T4ZO3ILL-Dl4AW2On.js} +4 -4
- package/dist/client/assets/gitGraphDiagram-UUTBAWPF-Dohcub9p.js +106 -0
- package/dist/client/assets/graph-C0KifbR6.js +1 -0
- package/dist/client/assets/index-Dl1nQm2g.js +778 -0
- package/dist/client/assets/index-G934YbUs.css +1 -0
- package/dist/client/assets/infoDiagram-42DDH7IO-BZjLv6IH.js +2 -0
- package/dist/client/assets/{ishikawaDiagram-PHBUUO56-DuT5_xy8.js → ishikawaDiagram-UXIWVN3A-rnfc72_m.js} +2 -2
- package/dist/client/assets/journeyDiagram-VCZTEJTY-DcW9mUFI.js +139 -0
- package/dist/client/assets/{kanban-definition-K7BYSVSG-ftVPxOyK.js → kanban-definition-6JOO6SKY-BgEOJRmr.js} +8 -8
- package/dist/client/assets/katex-DkKDou_j.js +257 -0
- package/dist/client/assets/layout-BQJpJX3E.js +1 -0
- package/dist/client/assets/{linear-BIb05kGQ.js → linear-4R4_f67K.js} +1 -1
- package/dist/client/assets/min-B78pX87t.js +1 -0
- package/dist/client/assets/mindmap-definition-QFDTVHPH-C34BXoiK.js +96 -0
- package/dist/client/assets/pieDiagram-DEJITSTG-ijiNKQ8e.js +30 -0
- package/dist/client/assets/{quadrantDiagram-337W2JSQ-kt-KJQMs.js → quadrantDiagram-34T5L4WZ-CZtSib2Y.js} +1 -1
- package/dist/client/assets/requirementDiagram-MS252O5E-BN-J-8AJ.js +84 -0
- package/dist/client/assets/{sankeyDiagram-WA2Y5GQK-CAokuuka.js → sankeyDiagram-XADWPNL6-CWP-qBhv.js} +1 -1
- package/dist/client/assets/sequenceDiagram-FGHM5R23-BnJnhdS6.js +157 -0
- package/dist/client/assets/stateDiagram-FHFEXIEX-DN4fDIGN.js +1 -0
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-D6Qg_ItE.js +1 -0
- package/dist/client/assets/timeline-definition-GMOUNBTQ-Ds6_LT-T.js +120 -0
- package/dist/client/assets/{vennDiagram-LZ73GAT5-C2MLaHWE.js → vennDiagram-DHZGUBPP-BrbuTJAI.js} +5 -5
- package/dist/client/assets/wardley-RL74JXVD-BsQbbPRZ.js +162 -0
- package/dist/client/assets/wardleyDiagram-NUSXRM2D-BH6aFYai.js +20 -0
- package/dist/client/assets/xychartDiagram-5P7HB3ND-BwK9W_yy.js +7 -0
- package/dist/client/index.html +2 -2
- package/dist/config.js +79 -77
- package/dist/server/api.js +353 -26
- package/dist/server/app.js +1 -1
- package/dist/server/index.js +19 -10
- package/dist/server/watcher.js +22 -7
- package/package.json +4 -1
- package/dist/client/assets/_basePickBy-CpIFiPmc.js +0 -1
- package/dist/client/assets/_baseUniq-C06JezLB.js +0 -1
- package/dist/client/assets/architectureDiagram-2XIMDMQ5-34IaPMNX.js +0 -36
- package/dist/client/assets/c4Diagram-IC4MRINW-g2IVuQJv.js +0 -10
- package/dist/client/assets/channel-DXsl4vhs.js +0 -1
- package/dist/client/assets/chunk-NQ4KR5QH-CsNzZMQ3.js +0 -220
- package/dist/client/assets/chunk-WL4C6EOR-DQcdvkib.js +0 -189
- package/dist/client/assets/classDiagram-VBA2DB6C-M0yhB_li.js +0 -1
- package/dist/client/assets/classDiagram-v2-RAHNMMFH-M0yhB_li.js +0 -1
- package/dist/client/assets/clone-DY0FLUFX.js +0 -1
- package/dist/client/assets/dagre-KLK3FWXG-I4ZL-Yjk.js +0 -4
- package/dist/client/assets/diagram-E7M64L7V-B5x_qiUv.js +0 -24
- package/dist/client/assets/diagram-IFDJBPK2-B3aH6gaM.js +0 -43
- package/dist/client/assets/diagram-P4PSJMXO-C65hl65m.js +0 -24
- package/dist/client/assets/erDiagram-INFDFZHY-CuGoj23m.js +0 -70
- package/dist/client/assets/flowDiagram-PKNHOUZH-BgI_bQaB.js +0 -162
- package/dist/client/assets/gitGraphDiagram-K3NZZRJ6-BQoXcJsF.js +0 -65
- package/dist/client/assets/graph-CGDYVHRC.js +0 -1
- package/dist/client/assets/index-CI8fw8c4.js +0 -709
- package/dist/client/assets/index-D2TYNMWH.css +0 -1
- package/dist/client/assets/infoDiagram-LFFYTUFH-Cua6joOv.js +0 -2
- package/dist/client/assets/journeyDiagram-4ABVD52K-My7-KJcV.js +0 -139
- package/dist/client/assets/katex-B1X10hvy.js +0 -261
- package/dist/client/assets/layout-DhQ7R7Ms.js +0 -1
- package/dist/client/assets/mindmap-definition-YRQLILUH-DwD8YJaZ.js +0 -68
- package/dist/client/assets/pieDiagram-SKSYHLDU-C-17c64G.js +0 -30
- package/dist/client/assets/requirementDiagram-Z7DCOOCP-BH89qJx9.js +0 -73
- package/dist/client/assets/sequenceDiagram-2WXFIKYE-CeVpL_7D.js +0 -145
- package/dist/client/assets/stateDiagram-RAJIS63D-DdqWd2rm.js +0 -1
- package/dist/client/assets/stateDiagram-v2-FVOUBMTO-B83j0Cq2.js +0 -1
- package/dist/client/assets/timeline-definition-YZTLITO2-BnlRy3AK.js +0 -61
- package/dist/client/assets/treemap-KZPCXAKY-xwXQmLuw.js +0 -162
- package/dist/client/assets/xychartDiagram-JWTSCODW-BiTzOgXT.js +0 -7
package/dist/server/api.js
CHANGED
|
@@ -1,16 +1,56 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
3
4
|
import path from 'path';
|
|
4
|
-
import
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import { saveConfig, DEFAULT_SENSITIVE_PATHS } from '../config.js';
|
|
7
|
+
import { minimatch } from 'minimatch';
|
|
5
8
|
function isAllowed(pathStr, config) {
|
|
6
9
|
const resolved = path.resolve(pathStr);
|
|
7
|
-
return resolved.startsWith(path.resolve(
|
|
10
|
+
return config.roots.some(root => resolved.startsWith(path.resolve(root.path)));
|
|
11
|
+
}
|
|
12
|
+
function validateRoot(rootPath, config) {
|
|
13
|
+
const resolved = path.resolve(rootPath);
|
|
14
|
+
const root = config.roots.find(r => path.resolve(r.path) === resolved);
|
|
15
|
+
return root ? path.resolve(root.path) : null;
|
|
16
|
+
}
|
|
17
|
+
function checkSensitivePath(inputPath) {
|
|
18
|
+
const basename = path.basename(inputPath);
|
|
19
|
+
for (const pattern of DEFAULT_SENSITIVE_PATHS) {
|
|
20
|
+
if (minimatch(basename, pattern, { nocase: true, dot: true }))
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
function checkNestedPath(newPath, existingRoots) {
|
|
26
|
+
const resolved = path.resolve(newPath);
|
|
27
|
+
for (const root of existingRoots) {
|
|
28
|
+
const existing = path.resolve(root.path);
|
|
29
|
+
if (resolved === existing)
|
|
30
|
+
return { isNested: true, conflictWith: root.path, reason: 'duplicate' };
|
|
31
|
+
if (resolved.startsWith(existing + path.sep))
|
|
32
|
+
return { isNested: true, conflictWith: root.path, reason: 'child' };
|
|
33
|
+
if (existing.startsWith(resolved + path.sep))
|
|
34
|
+
return { isNested: true, conflictWith: root.path, reason: 'parent' };
|
|
35
|
+
}
|
|
36
|
+
return { isNested: false };
|
|
37
|
+
}
|
|
38
|
+
function findRootForPath(filePath, config) {
|
|
39
|
+
for (const root of config.roots) {
|
|
40
|
+
const rootPath = path.resolve(root.path);
|
|
41
|
+
const relativePath = filePath.startsWith('/') ? filePath.slice(1) : filePath;
|
|
42
|
+
const fullPath = path.join(rootPath, relativePath);
|
|
43
|
+
if ((fullPath.startsWith(rootPath + path.sep) || fullPath === rootPath) && existsSync(fullPath)) {
|
|
44
|
+
return rootPath;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
8
48
|
}
|
|
9
49
|
function hasAllowedExtension(filename, extensions) {
|
|
10
50
|
const ext = path.extname(filename).toLowerCase();
|
|
11
51
|
return extensions.includes(ext);
|
|
12
52
|
}
|
|
13
|
-
async function walkDirectory(dir, config, matcher) {
|
|
53
|
+
async function walkDirectory(dir, rootPath, config, matcher) {
|
|
14
54
|
const nodes = [];
|
|
15
55
|
try {
|
|
16
56
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
@@ -22,11 +62,12 @@ async function walkDirectory(dir, config, matcher) {
|
|
|
22
62
|
if (matcher.isIgnored(fullPath, isDir))
|
|
23
63
|
continue;
|
|
24
64
|
if (isDir) {
|
|
25
|
-
const children = await walkDirectory(fullPath, config, matcher);
|
|
65
|
+
const children = await walkDirectory(fullPath, rootPath, config, matcher);
|
|
26
66
|
nodes.push({
|
|
27
67
|
name: entry.name,
|
|
28
|
-
path: fullPath.replace(
|
|
68
|
+
path: fullPath.replace(rootPath, '').replace(/\\/g, '/') || '/',
|
|
29
69
|
type: 'directory',
|
|
70
|
+
rootPath,
|
|
30
71
|
children,
|
|
31
72
|
});
|
|
32
73
|
}
|
|
@@ -34,15 +75,19 @@ async function walkDirectory(dir, config, matcher) {
|
|
|
34
75
|
if (hasAllowedExtension(entry.name, config.allowedExtensions)) {
|
|
35
76
|
nodes.push({
|
|
36
77
|
name: entry.name,
|
|
37
|
-
path: fullPath.replace(
|
|
78
|
+
path: fullPath.replace(rootPath, '').replace(/\\/g, '/') || '/',
|
|
38
79
|
type: 'file',
|
|
80
|
+
rootPath,
|
|
39
81
|
});
|
|
40
82
|
}
|
|
41
83
|
}
|
|
42
84
|
}
|
|
43
85
|
}
|
|
44
86
|
catch (e) {
|
|
45
|
-
// ignore
|
|
87
|
+
// Re-throw error if this is the root directory, otherwise ignore
|
|
88
|
+
if (dir === rootPath) {
|
|
89
|
+
throw e;
|
|
90
|
+
}
|
|
46
91
|
}
|
|
47
92
|
nodes.sort((a, b) => {
|
|
48
93
|
if (a.type === 'directory' && b.type === 'file')
|
|
@@ -72,7 +117,7 @@ export function createFileRouter(config, matcher) {
|
|
|
72
117
|
updates[key] = body[key];
|
|
73
118
|
}
|
|
74
119
|
}
|
|
75
|
-
|
|
120
|
+
saveConfig(config);
|
|
76
121
|
if (typeof updates.showHiddenFiles === 'boolean') {
|
|
77
122
|
config.showHiddenFiles = updates.showHiddenFiles;
|
|
78
123
|
}
|
|
@@ -99,10 +144,140 @@ export function createFileRouter(config, matcher) {
|
|
|
99
144
|
return c.json({ error: 'Failed to update config' }, 500);
|
|
100
145
|
}
|
|
101
146
|
});
|
|
147
|
+
// Root management routes
|
|
148
|
+
router.get('/roots', async (c) => {
|
|
149
|
+
return c.json({ roots: config.roots });
|
|
150
|
+
});
|
|
151
|
+
router.post('/roots', async (c) => {
|
|
152
|
+
try {
|
|
153
|
+
const body = await c.req.json();
|
|
154
|
+
const newPath = body.path;
|
|
155
|
+
if (!newPath)
|
|
156
|
+
return c.json({ error: 'Path is required' }, 400);
|
|
157
|
+
try {
|
|
158
|
+
const stat = await fs.stat(newPath);
|
|
159
|
+
if (!stat.isDirectory())
|
|
160
|
+
return c.json({ error: 'Path must be a directory' }, 400);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return c.json({ error: 'Path does not exist' }, 400);
|
|
164
|
+
}
|
|
165
|
+
if (checkSensitivePath(newPath))
|
|
166
|
+
return c.json({ error: 'Sensitive path not allowed' }, 400);
|
|
167
|
+
const nested = checkNestedPath(newPath, config.roots);
|
|
168
|
+
if (nested.isNested)
|
|
169
|
+
return c.json({ error: 'Nested path not allowed', conflictWith: nested.conflictWith, reason: nested.reason }, 400);
|
|
170
|
+
const newRoot = { path: path.resolve(newPath), exclude: body.exclude };
|
|
171
|
+
config.roots.push(newRoot);
|
|
172
|
+
saveConfig(config);
|
|
173
|
+
return c.json({ success: true, root: newRoot });
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
return c.json({ error: 'Failed to add root' }, 500);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
router.delete('/roots', async (c) => {
|
|
180
|
+
const pathParam = c.req.query('path');
|
|
181
|
+
if (!pathParam)
|
|
182
|
+
return c.json({ error: 'path parameter required' }, 400);
|
|
183
|
+
const idx = config.roots.findIndex(r => path.resolve(r.path) === path.resolve(pathParam));
|
|
184
|
+
if (idx === -1)
|
|
185
|
+
return c.json({ error: 'Root not found' }, 404);
|
|
186
|
+
config.roots.splice(idx, 1);
|
|
187
|
+
saveConfig(config);
|
|
188
|
+
return c.json({ success: true });
|
|
189
|
+
});
|
|
190
|
+
router.patch('/roots', async (c) => {
|
|
191
|
+
try {
|
|
192
|
+
const body = await c.req.json();
|
|
193
|
+
const { path: rootPath, exclude } = body;
|
|
194
|
+
if (!rootPath)
|
|
195
|
+
return c.json({ error: 'Path is required' }, 400);
|
|
196
|
+
const root = config.roots.find(r => path.resolve(r.path) === path.resolve(rootPath));
|
|
197
|
+
if (!root)
|
|
198
|
+
return c.json({ error: 'Root not found' }, 404);
|
|
199
|
+
if (exclude !== undefined)
|
|
200
|
+
root.exclude = exclude;
|
|
201
|
+
saveConfig(config);
|
|
202
|
+
return c.json({ success: true, root });
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
return c.json({ error: 'Failed to update root' }, 500);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
router.get('/roots/search', async (c) => {
|
|
209
|
+
const query = c.req.query('q') || '';
|
|
210
|
+
if (!query.trim())
|
|
211
|
+
return c.json({ matches: [] });
|
|
212
|
+
const matches = [];
|
|
213
|
+
let searchRoot;
|
|
214
|
+
let searchTerm;
|
|
215
|
+
if (query.startsWith('/')) {
|
|
216
|
+
const normalizedQuery = path.normalize(query);
|
|
217
|
+
const lastSlashIndex = normalizedQuery.lastIndexOf('/');
|
|
218
|
+
if (lastSlashIndex <= 0) {
|
|
219
|
+
searchRoot = '/';
|
|
220
|
+
searchTerm = normalizedQuery.slice(1);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
const dirPart = normalizedQuery.slice(0, lastSlashIndex) || '/';
|
|
224
|
+
const basePart = normalizedQuery.slice(lastSlashIndex + 1);
|
|
225
|
+
if (existsSync(dirPart) && (await fs.stat(dirPart)).isDirectory()) {
|
|
226
|
+
searchRoot = dirPart;
|
|
227
|
+
searchTerm = basePart;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
searchRoot = path.dirname(dirPart);
|
|
231
|
+
searchTerm = basePart;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
searchRoot = os.homedir();
|
|
237
|
+
searchTerm = query;
|
|
238
|
+
}
|
|
239
|
+
const pattern = `*${searchTerm}*`;
|
|
240
|
+
async function traverse(dir, depth) {
|
|
241
|
+
if (depth > 3 || matches.length >= 20)
|
|
242
|
+
return;
|
|
243
|
+
try {
|
|
244
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
245
|
+
for (const entry of entries) {
|
|
246
|
+
if (matches.length >= 20)
|
|
247
|
+
break;
|
|
248
|
+
if (entry.name.startsWith('.'))
|
|
249
|
+
continue;
|
|
250
|
+
const fullPath = path.join(dir, entry.name);
|
|
251
|
+
if (entry.isDirectory() && !checkSensitivePath(fullPath)) {
|
|
252
|
+
if (minimatch(entry.name, pattern, { nocase: true }))
|
|
253
|
+
matches.push(fullPath);
|
|
254
|
+
await traverse(fullPath, depth + 1);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch { }
|
|
259
|
+
}
|
|
260
|
+
await traverse(searchRoot, 0);
|
|
261
|
+
return c.json({ matches });
|
|
262
|
+
});
|
|
102
263
|
router.get('/', async (c) => {
|
|
103
264
|
try {
|
|
104
|
-
const
|
|
105
|
-
|
|
265
|
+
const groups = await Promise.all(config.roots.map(async (root) => {
|
|
266
|
+
try {
|
|
267
|
+
return {
|
|
268
|
+
root,
|
|
269
|
+
files: await walkDirectory(root.path, root.path, config, matcher)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
catch (e) {
|
|
273
|
+
return {
|
|
274
|
+
root,
|
|
275
|
+
files: [],
|
|
276
|
+
error: e instanceof Error ? e.message : 'Failed to read directory'
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}));
|
|
280
|
+
return c.json({ groups });
|
|
106
281
|
}
|
|
107
282
|
catch (e) {
|
|
108
283
|
return c.json({ error: 'Failed to read directory' }, 500);
|
|
@@ -116,7 +291,21 @@ export function createFileRouter(config, matcher) {
|
|
|
116
291
|
const paths = pathsParam.split(',').filter(Boolean);
|
|
117
292
|
const results = [];
|
|
118
293
|
for (const filePath of paths) {
|
|
119
|
-
const
|
|
294
|
+
const rootParam = c.req.query('root');
|
|
295
|
+
let rootPath;
|
|
296
|
+
if (rootParam) {
|
|
297
|
+
rootPath = path.resolve(rootParam);
|
|
298
|
+
if (!config.roots.some(r => path.resolve(r.path) === rootPath)) {
|
|
299
|
+
return c.json({ error: 'Invalid root' }, 400);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
rootPath = findRootForPath(filePath, config);
|
|
304
|
+
}
|
|
305
|
+
if (!rootPath)
|
|
306
|
+
return c.json({ error: 'Access denied' }, 403);
|
|
307
|
+
const relativePath = filePath.startsWith('/') ? filePath.slice(1) : filePath;
|
|
308
|
+
const fullPath = path.join(rootPath, relativePath);
|
|
120
309
|
if (!isAllowed(fullPath, config)) {
|
|
121
310
|
continue;
|
|
122
311
|
}
|
|
@@ -139,14 +328,53 @@ export function createFileRouter(config, matcher) {
|
|
|
139
328
|
});
|
|
140
329
|
router.get('/*', async (c) => {
|
|
141
330
|
const filePath = c.req.path.replace(/^\/api\/files/, '') || '/';
|
|
142
|
-
|
|
331
|
+
// Handle root path - return grouped file tree
|
|
332
|
+
if (filePath === '/' || filePath === '') {
|
|
333
|
+
try {
|
|
334
|
+
const groups = await Promise.all(config.roots.map(async (root) => {
|
|
335
|
+
try {
|
|
336
|
+
return {
|
|
337
|
+
root,
|
|
338
|
+
files: await walkDirectory(root.path, root.path, config, matcher)
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
catch (e) {
|
|
342
|
+
return {
|
|
343
|
+
root,
|
|
344
|
+
files: [],
|
|
345
|
+
error: e instanceof Error ? e.message : 'Failed to read directory'
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}));
|
|
349
|
+
return c.json({ groups });
|
|
350
|
+
}
|
|
351
|
+
catch (e) {
|
|
352
|
+
return c.json({ error: 'Failed to read directory' }, 500);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Handle root parameter from query string
|
|
356
|
+
const rootParam = c.req.query('root');
|
|
357
|
+
let rootPath;
|
|
358
|
+
if (rootParam) {
|
|
359
|
+
rootPath = path.resolve(rootParam);
|
|
360
|
+
if (!config.roots.some(r => path.resolve(r.path) === rootPath)) {
|
|
361
|
+
return c.json({ error: 'Invalid root' }, 400);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
rootPath = findRootForPath(filePath, config);
|
|
366
|
+
}
|
|
367
|
+
if (!rootPath)
|
|
368
|
+
return c.json({ error: 'Access denied' }, 403);
|
|
369
|
+
const relativePath = filePath.startsWith('/') ? filePath.slice(1) : filePath;
|
|
370
|
+
const fullPath = path.join(rootPath, relativePath);
|
|
143
371
|
if (!isAllowed(fullPath, config)) {
|
|
144
372
|
return c.json({ error: 'Access denied' }, 403);
|
|
145
373
|
}
|
|
146
374
|
try {
|
|
147
375
|
const stat = await fs.stat(fullPath);
|
|
148
376
|
if (stat.isDirectory()) {
|
|
149
|
-
const files = await walkDirectory(fullPath, config, matcher);
|
|
377
|
+
const files = await walkDirectory(fullPath, rootPath, config, matcher);
|
|
150
378
|
return c.json({ files });
|
|
151
379
|
}
|
|
152
380
|
}
|
|
@@ -161,9 +389,62 @@ export function createFileRouter(config, matcher) {
|
|
|
161
389
|
return c.json({ error: 'File not found' }, 404);
|
|
162
390
|
}
|
|
163
391
|
});
|
|
392
|
+
router.post('/copy', async (c) => {
|
|
393
|
+
try {
|
|
394
|
+
const body = await c.req.json();
|
|
395
|
+
let { sourcePath, targetPath, sourceRoot, targetRoot } = body;
|
|
396
|
+
if (!sourcePath || !targetPath) {
|
|
397
|
+
return c.json({ error: 'sourcePath and targetPath required' }, 400);
|
|
398
|
+
}
|
|
399
|
+
if (!sourceRoot || !targetRoot) {
|
|
400
|
+
return c.json({ error: 'sourceRoot and targetRoot required' }, 400);
|
|
401
|
+
}
|
|
402
|
+
const validatedSourceRoot = validateRoot(sourceRoot, config);
|
|
403
|
+
const validatedTargetRoot = validateRoot(targetRoot, config);
|
|
404
|
+
if (!validatedSourceRoot)
|
|
405
|
+
return c.json({ error: 'Invalid source root' }, 400);
|
|
406
|
+
if (!validatedTargetRoot)
|
|
407
|
+
return c.json({ error: 'Invalid target root' }, 400);
|
|
408
|
+
const srcFullPath = path.join(validatedSourceRoot, sourcePath);
|
|
409
|
+
let tgtFullPath = path.join(validatedTargetRoot, targetPath);
|
|
410
|
+
const stat = await fs.stat(srcFullPath);
|
|
411
|
+
const targetExists = await fs.access(tgtFullPath).then(() => true).catch(() => false);
|
|
412
|
+
if (targetExists) {
|
|
413
|
+
const ext = path.extname(targetPath);
|
|
414
|
+
const base = path.basename(targetPath, ext);
|
|
415
|
+
const dir = path.dirname(targetPath);
|
|
416
|
+
targetPath = `${dir}/${base} (copy)${ext}`;
|
|
417
|
+
tgtFullPath = path.join(validatedTargetRoot, targetPath);
|
|
418
|
+
}
|
|
419
|
+
if (stat.isDirectory()) {
|
|
420
|
+
await fs.cp(srcFullPath, tgtFullPath, { recursive: true });
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
await fs.copyFile(srcFullPath, tgtFullPath);
|
|
424
|
+
}
|
|
425
|
+
return c.json({ success: true, newPath: targetPath });
|
|
426
|
+
}
|
|
427
|
+
catch (e) {
|
|
428
|
+
console.error('Copy error:', e);
|
|
429
|
+
return c.json({ error: 'Failed to copy' }, 500);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
164
432
|
router.post('/*', async (c) => {
|
|
165
433
|
const filePath = c.req.path.replace(/^\/api\/files/, '') || '/';
|
|
166
|
-
const
|
|
434
|
+
const rootParam = c.req.query('root');
|
|
435
|
+
let rootPath;
|
|
436
|
+
if (rootParam) {
|
|
437
|
+
rootPath = validateRoot(rootParam, config);
|
|
438
|
+
if (!rootPath)
|
|
439
|
+
return c.json({ error: 'Invalid root' }, 400);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
rootPath = findRootForPath(filePath, config);
|
|
443
|
+
if (!rootPath)
|
|
444
|
+
return c.json({ error: 'Access denied' }, 403);
|
|
445
|
+
}
|
|
446
|
+
const relativePath = filePath.startsWith('/') ? filePath.slice(1) : filePath;
|
|
447
|
+
const fullPath = path.join(rootPath, relativePath);
|
|
167
448
|
if (!isAllowed(fullPath, config)) {
|
|
168
449
|
return c.json({ error: 'Access denied' }, 403);
|
|
169
450
|
}
|
|
@@ -173,7 +454,19 @@ export function createFileRouter(config, matcher) {
|
|
|
173
454
|
if (body.type === 'create') {
|
|
174
455
|
const parentPath = body.parentPath || '';
|
|
175
456
|
const name = body.name;
|
|
176
|
-
|
|
457
|
+
let parentRootPath;
|
|
458
|
+
if (body.root) {
|
|
459
|
+
parentRootPath = validateRoot(body.root, config);
|
|
460
|
+
if (!parentRootPath) {
|
|
461
|
+
return c.json({ error: 'Invalid root' }, 400);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
parentRootPath = findRootForPath(parentPath, config);
|
|
466
|
+
if (!parentRootPath)
|
|
467
|
+
return c.json({ error: 'Access denied' }, 403);
|
|
468
|
+
}
|
|
469
|
+
const targetPath = path.join(parentRootPath, parentPath, name);
|
|
177
470
|
if (!isAllowed(targetPath, config)) {
|
|
178
471
|
return c.json({ error: 'Access denied' }, 403);
|
|
179
472
|
}
|
|
@@ -187,7 +480,7 @@ export function createFileRouter(config, matcher) {
|
|
|
187
480
|
}
|
|
188
481
|
await fs.writeFile(targetPath, '', 'utf-8');
|
|
189
482
|
}
|
|
190
|
-
return c.json({ success: true, path: targetPath.replace(
|
|
483
|
+
return c.json({ success: true, path: targetPath.replace(parentRootPath, '') });
|
|
191
484
|
}
|
|
192
485
|
catch (e) {
|
|
193
486
|
return c.json({ error: 'Failed to create' }, 500);
|
|
@@ -205,19 +498,23 @@ export function createFileRouter(config, matcher) {
|
|
|
205
498
|
}
|
|
206
499
|
});
|
|
207
500
|
router.put('/*', async (c) => {
|
|
208
|
-
const filePath = c.req.path.replace(/^\/api\/files/, '') || '/';
|
|
209
|
-
const fullPath = path.join(config.root, filePath);
|
|
210
|
-
if (!isAllowed(fullPath, config)) {
|
|
211
|
-
return c.json({ error: 'Access denied' }, 403);
|
|
212
|
-
}
|
|
213
501
|
try {
|
|
214
502
|
const body = await c.req.json();
|
|
215
|
-
const { oldPath, newPath, isDirectory } = body;
|
|
503
|
+
const { oldPath, newPath, isDirectory, sourceRoot, targetRoot } = body;
|
|
216
504
|
if (!oldPath || !newPath) {
|
|
217
505
|
return c.json({ error: 'oldPath and newPath are required' }, 400);
|
|
218
506
|
}
|
|
219
|
-
|
|
220
|
-
|
|
507
|
+
if (!sourceRoot || !targetRoot) {
|
|
508
|
+
return c.json({ error: 'sourceRoot and targetRoot are required' }, 400);
|
|
509
|
+
}
|
|
510
|
+
const validatedSourceRoot = validateRoot(sourceRoot, config);
|
|
511
|
+
const validatedTargetRoot = validateRoot(targetRoot, config);
|
|
512
|
+
if (!validatedSourceRoot)
|
|
513
|
+
return c.json({ error: 'Invalid source root' }, 400);
|
|
514
|
+
if (!validatedTargetRoot)
|
|
515
|
+
return c.json({ error: 'Invalid target root' }, 400);
|
|
516
|
+
const oldFullPath = path.join(validatedSourceRoot, oldPath);
|
|
517
|
+
const newFullPath = path.join(validatedTargetRoot, newPath);
|
|
221
518
|
if (!isAllowed(oldFullPath, config) || !isAllowed(newFullPath, config)) {
|
|
222
519
|
return c.json({ error: 'Access denied' }, 403);
|
|
223
520
|
}
|
|
@@ -226,15 +523,32 @@ export function createFileRouter(config, matcher) {
|
|
|
226
523
|
if (!stat.isDirectory()) {
|
|
227
524
|
return c.json({ error: 'Source is not a directory' }, 400);
|
|
228
525
|
}
|
|
229
|
-
await fs.rename(oldFullPath, newFullPath);
|
|
230
526
|
}
|
|
231
527
|
else {
|
|
232
528
|
const ext = path.extname(newPath).toLowerCase();
|
|
233
529
|
if (!hasAllowedExtension(newPath, config.allowedExtensions)) {
|
|
234
530
|
return c.json({ error: 'File type not allowed' }, 400);
|
|
235
531
|
}
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
236
534
|
await fs.rename(oldFullPath, newFullPath);
|
|
237
535
|
}
|
|
536
|
+
catch (err) {
|
|
537
|
+
if (err instanceof Error && 'code' in err && err.code === 'EXDEV') {
|
|
538
|
+
// Cross-filesystem move - copy then delete
|
|
539
|
+
if (isDirectory) {
|
|
540
|
+
await fs.cp(oldFullPath, newFullPath, { recursive: true });
|
|
541
|
+
await fs.rm(oldFullPath, { recursive: true });
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
await fs.copyFile(oldFullPath, newFullPath);
|
|
545
|
+
await fs.unlink(oldFullPath);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
throw err;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
238
552
|
return c.json({ success: true, newPath });
|
|
239
553
|
}
|
|
240
554
|
catch (e) {
|
|
@@ -244,7 +558,20 @@ export function createFileRouter(config, matcher) {
|
|
|
244
558
|
});
|
|
245
559
|
router.delete('/*', async (c) => {
|
|
246
560
|
const filePath = c.req.path.replace(/^\/api\/files/, '') || '/';
|
|
247
|
-
const
|
|
561
|
+
const rootParam = c.req.query('root');
|
|
562
|
+
let rootPath;
|
|
563
|
+
if (rootParam) {
|
|
564
|
+
rootPath = validateRoot(rootParam, config);
|
|
565
|
+
if (!rootPath)
|
|
566
|
+
return c.json({ error: 'Invalid root' }, 400);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
rootPath = findRootForPath(filePath, config);
|
|
570
|
+
if (!rootPath)
|
|
571
|
+
return c.json({ error: 'Access denied' }, 403);
|
|
572
|
+
}
|
|
573
|
+
const relativePath = filePath.startsWith('/') ? filePath.slice(1) : filePath;
|
|
574
|
+
const fullPath = path.join(rootPath, relativePath);
|
|
248
575
|
if (!isAllowed(fullPath, config)) {
|
|
249
576
|
return c.json({ error: 'Access denied' }, 403);
|
|
250
577
|
}
|
package/dist/server/app.js
CHANGED
|
@@ -9,7 +9,7 @@ export function createApp(config) {
|
|
|
9
9
|
ignoreFileNames: config.ignore.ignoreFileNames,
|
|
10
10
|
globalPatterns: config.ignore.patterns,
|
|
11
11
|
};
|
|
12
|
-
const matcher = new IgnoreMatcher(config.
|
|
12
|
+
const matcher = new IgnoreMatcher(config.roots[0]?.path || process.cwd(), ignoreConfig);
|
|
13
13
|
app.use('*', cors());
|
|
14
14
|
app.use('*', async (c, next) => {
|
|
15
15
|
c.set('config', config);
|
package/dist/server/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { serve } from '@hono/node-server';
|
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
3
|
import { cors } from 'hono/cors';
|
|
4
4
|
import { createFileRouter } from './api.js';
|
|
5
|
-
import { loadConfig } from '../config.js';
|
|
5
|
+
import { loadConfig, DEFAULT_PORT, DEFAULT_HOST } from '../config.js';
|
|
6
6
|
import { setupWatcher } from './watcher.js';
|
|
7
7
|
import { IgnoreMatcher } from './ignore.js';
|
|
8
8
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
@@ -11,9 +11,17 @@ import path from 'path';
|
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
13
|
const clientDir = path.join(__dirname, '..', 'client');
|
|
14
|
+
function findRootForPath(filePath, config) {
|
|
15
|
+
for (const root of config.roots) {
|
|
16
|
+
if (filePath.startsWith(root.path)) {
|
|
17
|
+
return root.path;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return config.roots[0]?.path || '';
|
|
21
|
+
}
|
|
14
22
|
async function main() {
|
|
15
23
|
const config = await loadConfig();
|
|
16
|
-
const matcher = new IgnoreMatcher(config.
|
|
24
|
+
const matcher = new IgnoreMatcher(config.roots[0]?.path || process.cwd(), {
|
|
17
25
|
enableIgnoreFiles: config.ignore.enableIgnoreFiles,
|
|
18
26
|
ignoreFileNames: config.ignore.ignoreFileNames,
|
|
19
27
|
globalPatterns: config.ignore.patterns,
|
|
@@ -69,8 +77,8 @@ async function main() {
|
|
|
69
77
|
});
|
|
70
78
|
const server = serve({
|
|
71
79
|
fetch: app.fetch,
|
|
72
|
-
port:
|
|
73
|
-
hostname:
|
|
80
|
+
port: DEFAULT_PORT,
|
|
81
|
+
hostname: DEFAULT_HOST,
|
|
74
82
|
});
|
|
75
83
|
const clients = new Set();
|
|
76
84
|
const wss = new WebSocketServer({ noServer: true });
|
|
@@ -86,9 +94,10 @@ async function main() {
|
|
|
86
94
|
}
|
|
87
95
|
});
|
|
88
96
|
setupWatcher(config, matcher, {
|
|
89
|
-
onFileChange: (event, filePath) => {
|
|
90
|
-
const
|
|
91
|
-
const
|
|
97
|
+
onFileChange: (rootPath, event, filePath) => {
|
|
98
|
+
const actualRootPath = findRootForPath(filePath, config);
|
|
99
|
+
const relativePath = filePath.replace(actualRootPath, '');
|
|
100
|
+
const message = JSON.stringify({ type: 'file:change', event, path: relativePath, rootPath: actualRootPath });
|
|
92
101
|
clients.forEach((client) => {
|
|
93
102
|
if (client.readyState === WebSocket.OPEN) {
|
|
94
103
|
client.send(message);
|
|
@@ -97,9 +106,9 @@ async function main() {
|
|
|
97
106
|
},
|
|
98
107
|
});
|
|
99
108
|
console.log(`\n ColonyNote is running!\n`);
|
|
100
|
-
console.log(` Local: http://localhost:${
|
|
101
|
-
console.log(` Network: http://${
|
|
102
|
-
console.log(`
|
|
109
|
+
console.log(` Local: http://localhost:${DEFAULT_PORT}`);
|
|
110
|
+
console.log(` Network: http://${DEFAULT_HOST}:${DEFAULT_PORT}`);
|
|
111
|
+
console.log(` Roots: ${config.roots.map(r => r.path).join(', ')}\n`);
|
|
103
112
|
}
|
|
104
113
|
main().catch((e) => {
|
|
105
114
|
console.error('Failed to start:', e);
|
package/dist/server/watcher.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import chokidar from 'chokidar';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
export function setupWatcher(config, matcher, callbacks) {
|
|
4
|
-
const
|
|
4
|
+
const rootPaths = config.roots.map(r => r.path);
|
|
5
|
+
const watcher = chokidar.watch(rootPaths, {
|
|
5
6
|
ignored: (filePath) => {
|
|
6
7
|
if (!config.showHiddenFiles && (filePath.includes('/.') || filePath.startsWith('.')))
|
|
7
8
|
return true;
|
|
@@ -22,26 +23,40 @@ export function setupWatcher(config, matcher, callbacks) {
|
|
|
22
23
|
},
|
|
23
24
|
persistent: true,
|
|
24
25
|
ignoreInitial: true,
|
|
25
|
-
depth:
|
|
26
|
+
depth: 3,
|
|
26
27
|
});
|
|
27
28
|
watcher
|
|
28
29
|
.on('add', (path) => {
|
|
29
30
|
if (config.allowedExtensions.some(ext => path.endsWith(ext))) {
|
|
30
|
-
|
|
31
|
+
const matchingRoot = config.roots.find(r => path.startsWith(r.path));
|
|
32
|
+
const rootPath = matchingRoot?.path || config.roots[0]?.path || '';
|
|
33
|
+
callbacks.onFileChange(rootPath, 'add', path);
|
|
31
34
|
}
|
|
32
35
|
})
|
|
33
36
|
.on('change', (path) => {
|
|
34
37
|
if (config.allowedExtensions.some(ext => path.endsWith(ext))) {
|
|
35
|
-
|
|
38
|
+
const matchingRoot = config.roots.find(r => path.startsWith(r.path));
|
|
39
|
+
const rootPath = matchingRoot?.path || config.roots[0]?.path || '';
|
|
40
|
+
callbacks.onFileChange(rootPath, 'change', path);
|
|
36
41
|
}
|
|
37
42
|
})
|
|
38
43
|
.on('unlink', (path) => {
|
|
39
44
|
if (config.allowedExtensions.some(ext => path.endsWith(ext))) {
|
|
40
|
-
|
|
45
|
+
const matchingRoot = config.roots.find(r => path.startsWith(r.path));
|
|
46
|
+
const rootPath = matchingRoot?.path || config.roots[0]?.path || '';
|
|
47
|
+
callbacks.onFileChange(rootPath, 'unlink', path);
|
|
41
48
|
}
|
|
42
49
|
})
|
|
43
|
-
.on('addDir', (path) =>
|
|
44
|
-
.
|
|
50
|
+
.on('addDir', (path) => {
|
|
51
|
+
const matchingRoot = config.roots.find(r => path.startsWith(r.path));
|
|
52
|
+
const rootPath = matchingRoot?.path || config.roots[0]?.path || '';
|
|
53
|
+
callbacks.onFileChange(rootPath, 'addDir', path);
|
|
54
|
+
})
|
|
55
|
+
.on('unlinkDir', (path) => {
|
|
56
|
+
const matchingRoot = config.roots.find(r => path.startsWith(r.path));
|
|
57
|
+
const rootPath = matchingRoot?.path || config.roots[0]?.path || '';
|
|
58
|
+
callbacks.onFileChange(rootPath, 'unlinkDir', path);
|
|
59
|
+
})
|
|
45
60
|
.on('error', (error) => console.error('Watcher error:', error));
|
|
46
61
|
return watcher;
|
|
47
62
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "colonynote",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.13",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=18"
|
|
6
6
|
},
|
|
@@ -65,6 +65,9 @@
|
|
|
65
65
|
"chokidar": "^4.0.3",
|
|
66
66
|
"class-variance-authority": "^0.7.1",
|
|
67
67
|
"clsx": "^2.1.1",
|
|
68
|
+
"cmdk": "^1.1.1",
|
|
69
|
+
"colonynote": "1.0.0-beta.10",
|
|
70
|
+
"commander": "^14.0.3",
|
|
68
71
|
"flexsearch": "^0.8.212",
|
|
69
72
|
"hono": "^4.12.9",
|
|
70
73
|
"katex": "^0.16.40",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{e as x,c as O,g as m,k as P,h as p,j as w,l as c,m as A,n as I,t as N,o as _}from"./_baseUniq-C06JezLB.js";import{aU as g,aC as $,aV as E,aW as F,aX as M,aY as l,aZ as B,a_ as T,a$ as y,b0 as S}from"./index-CI8fw8c4.js";var C=/\s/;function G(n){for(var r=n.length;r--&&C.test(n.charAt(r)););return r}var H=/^\s+/;function L(n){return n&&n.slice(0,G(n)+1).replace(H,"")}var o=NaN,R=/^[-+]0x[0-9a-f]+$/i,W=/^0b[01]+$/i,X=/^0o[0-7]+$/i,Y=parseInt;function q(n){if(typeof n=="number")return n;if(x(n))return o;if(g(n)){var r=typeof n.valueOf=="function"?n.valueOf():n;n=g(r)?r+"":r}if(typeof n!="string")return n===0?n:+n;n=L(n);var t=W.test(n);return t||X.test(n)?Y(n.slice(2),t?2:8):R.test(n)?o:+n}var v=1/0,z=17976931348623157e292;function K(n){if(!n)return n===0?n:0;if(n=q(n),n===v||n===-v){var r=n<0?-1:1;return r*z}return n===n?n:0}function U(n){var r=K(n),t=r%1;return r===r?t?r-t:r:0}function fn(n){var r=n==null?0:n.length;return r?O(n):[]}var b=Object.prototype,Z=b.hasOwnProperty,dn=$(function(n,r){n=Object(n);var t=-1,i=r.length,a=i>2?r[2]:void 0;for(a&&E(r[0],r[1],a)&&(i=1);++t<i;)for(var f=r[t],e=F(f),s=-1,d=e.length;++s<d;){var u=e[s],h=n[u];(h===void 0||M(h,b[u])&&!Z.call(n,u))&&(n[u]=f[u])}return n});function un(n){var r=n==null?0:n.length;return r?n[r-1]:void 0}function D(n){return function(r,t,i){var a=Object(r);if(!l(r)){var f=m(t);r=P(r),t=function(s){return f(a[s],s,a)}}var e=n(r,t,i);return e>-1?a[f?r[e]:e]:void 0}}var J=Math.max;function Q(n,r,t){var i=n==null?0:n.length;if(!i)return-1;var a=t==null?0:U(t);return a<0&&(a=J(i+a,0)),p(n,m(r),a)}var hn=D(Q);function V(n,r){var t=-1,i=l(n)?Array(n.length):[];return w(n,function(a,f,e){i[++t]=r(a,f,e)}),i}function gn(n,r){var t=B(n)?c:V;return t(n,m(r))}var j=Object.prototype,k=j.hasOwnProperty;function nn(n,r){return n!=null&&k.call(n,r)}function mn(n,r){return n!=null&&A(n,r,nn)}function rn(n,r){return n<r}function tn(n,r,t){for(var i=-1,a=n.length;++i<a;){var f=n[i],e=r(f);if(e!=null&&(s===void 0?e===e&&!x(e):t(e,s)))var s=e,d=f}return d}function on(n){return n&&n.length?tn(n,T,rn):void 0}function an(n,r,t,i){if(!g(n))return n;r=I(r,n);for(var a=-1,f=r.length,e=f-1,s=n;s!=null&&++a<f;){var d=N(r[a]),u=t;if(d==="__proto__"||d==="constructor"||d==="prototype")return n;if(a!=e){var h=s[d];u=void 0,u===void 0&&(u=g(h)?h:y(r[a+1])?[]:{})}S(s,d,u),s=s[d]}return n}function vn(n,r,t){for(var i=-1,a=r.length,f={};++i<a;){var e=r[i],s=_(n,e);t(s,e)&&an(f,I(e,n),s)}return f}export{rn as a,tn as b,V as c,vn as d,on as e,fn as f,hn as g,mn as h,dn as i,U as j,un as l,gn as m,K as t};
|