@enactprotocol/shared 2.2.2 → 2.2.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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +44 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +95 -2
- package/dist/registry.js.map +1 -1
- package/dist/resolver.d.ts +1 -1
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +12 -3
- package/dist/resolver.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +6 -0
- package/src/registry.ts +131 -2
- package/src/resolver.ts +14 -3
- package/tests/registry.test.ts +195 -0
- package/tests/resolver.test.ts +80 -0
- package/tsconfig.tsbuildinfo +1 -1
package/tests/registry.test.ts
CHANGED
|
@@ -6,14 +6,19 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
|
6
6
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import {
|
|
9
|
+
addAlias,
|
|
9
10
|
addToolToRegistry,
|
|
11
|
+
getAliasesForTool,
|
|
10
12
|
getInstalledVersion,
|
|
11
13
|
getToolCachePath,
|
|
12
14
|
getToolsJsonPath,
|
|
13
15
|
isToolInstalled,
|
|
14
16
|
listInstalledTools,
|
|
15
17
|
loadToolsRegistry,
|
|
18
|
+
removeAlias,
|
|
19
|
+
removeAliasesForTool,
|
|
16
20
|
removeToolFromRegistry,
|
|
21
|
+
resolveAlias,
|
|
17
22
|
saveToolsRegistry,
|
|
18
23
|
} from "../src/registry";
|
|
19
24
|
|
|
@@ -228,4 +233,194 @@ describe("registry", () => {
|
|
|
228
233
|
expect(tools.length).toBe(0);
|
|
229
234
|
});
|
|
230
235
|
});
|
|
236
|
+
|
|
237
|
+
describe("addAlias", () => {
|
|
238
|
+
test("adds alias to registry", () => {
|
|
239
|
+
addToolToRegistry("test/aliased-tool", "1.0.0", "project", PROJECT_DIR);
|
|
240
|
+
addAlias("mytool", "test/aliased-tool", "project", PROJECT_DIR);
|
|
241
|
+
|
|
242
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
243
|
+
expect(registry.aliases?.mytool).toBe("test/aliased-tool");
|
|
244
|
+
|
|
245
|
+
// Clean up
|
|
246
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("throws error when alias already exists for different tool", () => {
|
|
250
|
+
addToolToRegistry("test/tool1", "1.0.0", "project", PROJECT_DIR);
|
|
251
|
+
addToolToRegistry("test/tool2", "1.0.0", "project", PROJECT_DIR);
|
|
252
|
+
addAlias("shared", "test/tool1", "project", PROJECT_DIR);
|
|
253
|
+
|
|
254
|
+
expect(() => {
|
|
255
|
+
addAlias("shared", "test/tool2", "project", PROJECT_DIR);
|
|
256
|
+
}).toThrow('Alias "shared" already exists for tool "test/tool1"');
|
|
257
|
+
|
|
258
|
+
// Clean up
|
|
259
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("allows adding same alias for same tool (idempotent)", () => {
|
|
263
|
+
addToolToRegistry("test/same-tool", "1.0.0", "project", PROJECT_DIR);
|
|
264
|
+
addAlias("same", "test/same-tool", "project", PROJECT_DIR);
|
|
265
|
+
|
|
266
|
+
// Should not throw
|
|
267
|
+
expect(() => {
|
|
268
|
+
addAlias("same", "test/same-tool", "project", PROJECT_DIR);
|
|
269
|
+
}).not.toThrow();
|
|
270
|
+
|
|
271
|
+
// Clean up
|
|
272
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("removeAlias", () => {
|
|
277
|
+
test("removes existing alias", () => {
|
|
278
|
+
addToolToRegistry("test/removable", "1.0.0", "project", PROJECT_DIR);
|
|
279
|
+
addAlias("removeme", "test/removable", "project", PROJECT_DIR);
|
|
280
|
+
|
|
281
|
+
const removed = removeAlias("removeme", "project", PROJECT_DIR);
|
|
282
|
+
expect(removed).toBe(true);
|
|
283
|
+
|
|
284
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
285
|
+
expect(registry.aliases?.removeme).toBeUndefined();
|
|
286
|
+
|
|
287
|
+
// Clean up
|
|
288
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("returns false for non-existent alias", () => {
|
|
292
|
+
const removed = removeAlias("nonexistent", "project", PROJECT_DIR);
|
|
293
|
+
expect(removed).toBe(false);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe("resolveAlias", () => {
|
|
298
|
+
test("resolves existing alias to tool name", () => {
|
|
299
|
+
addToolToRegistry("org/category/full-name", "1.0.0", "project", PROJECT_DIR);
|
|
300
|
+
addAlias("short", "org/category/full-name", "project", PROJECT_DIR);
|
|
301
|
+
|
|
302
|
+
const resolved = resolveAlias("short", "project", PROJECT_DIR);
|
|
303
|
+
expect(resolved).toBe("org/category/full-name");
|
|
304
|
+
|
|
305
|
+
// Clean up
|
|
306
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("returns null for non-existent alias", () => {
|
|
310
|
+
const resolved = resolveAlias("unknown", "project", PROJECT_DIR);
|
|
311
|
+
expect(resolved).toBeNull();
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("getAliasesForTool", () => {
|
|
316
|
+
test("returns all aliases for a tool", () => {
|
|
317
|
+
addToolToRegistry("test/multi-alias", "1.0.0", "project", PROJECT_DIR);
|
|
318
|
+
addAlias("alias1", "test/multi-alias", "project", PROJECT_DIR);
|
|
319
|
+
addAlias("alias2", "test/multi-alias", "project", PROJECT_DIR);
|
|
320
|
+
|
|
321
|
+
const aliases = getAliasesForTool("test/multi-alias", "project", PROJECT_DIR);
|
|
322
|
+
expect(aliases).toContain("alias1");
|
|
323
|
+
expect(aliases).toContain("alias2");
|
|
324
|
+
expect(aliases.length).toBe(2);
|
|
325
|
+
|
|
326
|
+
// Clean up
|
|
327
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("returns empty array for tool without aliases", () => {
|
|
331
|
+
addToolToRegistry("test/no-alias", "1.0.0", "project", PROJECT_DIR);
|
|
332
|
+
|
|
333
|
+
const aliases = getAliasesForTool("test/no-alias", "project", PROJECT_DIR);
|
|
334
|
+
expect(aliases).toEqual([]);
|
|
335
|
+
|
|
336
|
+
// Clean up
|
|
337
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe("removeAliasesForTool", () => {
|
|
342
|
+
test("removes all aliases for a tool", () => {
|
|
343
|
+
addToolToRegistry("test/cleanup", "1.0.0", "project", PROJECT_DIR);
|
|
344
|
+
addAlias("cleanup1", "test/cleanup", "project", PROJECT_DIR);
|
|
345
|
+
addAlias("cleanup2", "test/cleanup", "project", PROJECT_DIR);
|
|
346
|
+
|
|
347
|
+
const removed = removeAliasesForTool("test/cleanup", "project", PROJECT_DIR);
|
|
348
|
+
expect(removed).toBe(2);
|
|
349
|
+
|
|
350
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
351
|
+
expect(registry.aliases?.cleanup1).toBeUndefined();
|
|
352
|
+
expect(registry.aliases?.cleanup2).toBeUndefined();
|
|
353
|
+
|
|
354
|
+
// Clean up
|
|
355
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("returns 0 for tool without aliases", () => {
|
|
359
|
+
addToolToRegistry("test/no-aliases-to-remove", "1.0.0", "project", PROJECT_DIR);
|
|
360
|
+
|
|
361
|
+
const removed = removeAliasesForTool("test/no-aliases-to-remove", "project", PROJECT_DIR);
|
|
362
|
+
expect(removed).toBe(0);
|
|
363
|
+
|
|
364
|
+
// Clean up
|
|
365
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("does not remove aliases for other tools", () => {
|
|
369
|
+
addToolToRegistry("test/keep", "1.0.0", "project", PROJECT_DIR);
|
|
370
|
+
addToolToRegistry("test/remove", "1.0.0", "project", PROJECT_DIR);
|
|
371
|
+
addAlias("keepme", "test/keep", "project", PROJECT_DIR);
|
|
372
|
+
addAlias("removeme", "test/remove", "project", PROJECT_DIR);
|
|
373
|
+
|
|
374
|
+
removeAliasesForTool("test/remove", "project", PROJECT_DIR);
|
|
375
|
+
|
|
376
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
377
|
+
expect(registry.aliases?.keepme).toBe("test/keep");
|
|
378
|
+
expect(registry.aliases?.removeme).toBeUndefined();
|
|
379
|
+
|
|
380
|
+
// Clean up
|
|
381
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"));
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe("loadToolsRegistry with aliases", () => {
|
|
386
|
+
test("loads existing registry with aliases", () => {
|
|
387
|
+
const registryPath = join(PROJECT_ENACT_DIR, "tools.json");
|
|
388
|
+
writeFileSync(
|
|
389
|
+
registryPath,
|
|
390
|
+
JSON.stringify({
|
|
391
|
+
tools: {
|
|
392
|
+
"test/tool": "1.0.0",
|
|
393
|
+
},
|
|
394
|
+
aliases: {
|
|
395
|
+
t: "test/tool",
|
|
396
|
+
},
|
|
397
|
+
})
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
401
|
+
expect(registry.tools["test/tool"]).toBe("1.0.0");
|
|
402
|
+
expect(registry.aliases?.t).toBe("test/tool");
|
|
403
|
+
|
|
404
|
+
// Clean up
|
|
405
|
+
rmSync(registryPath);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test("returns empty aliases when not present in file", () => {
|
|
409
|
+
const registryPath = join(PROJECT_ENACT_DIR, "tools.json");
|
|
410
|
+
writeFileSync(
|
|
411
|
+
registryPath,
|
|
412
|
+
JSON.stringify({
|
|
413
|
+
tools: {
|
|
414
|
+
"test/tool": "1.0.0",
|
|
415
|
+
},
|
|
416
|
+
})
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
const registry = loadToolsRegistry("project", PROJECT_DIR);
|
|
420
|
+
expect(registry.aliases).toEqual({});
|
|
421
|
+
|
|
422
|
+
// Clean up
|
|
423
|
+
rmSync(registryPath);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
231
426
|
});
|
package/tests/resolver.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
2
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { addAlias, addToolToRegistry, removeAlias } from "../src/registry";
|
|
4
5
|
import {
|
|
5
6
|
ToolResolveError,
|
|
6
7
|
getToolPath,
|
|
@@ -269,4 +270,83 @@ Documentation here.
|
|
|
269
270
|
expect(error.searchedLocations).toEqual(["/path/1", "/path/2"]);
|
|
270
271
|
});
|
|
271
272
|
});
|
|
273
|
+
|
|
274
|
+
describe("alias resolution", () => {
|
|
275
|
+
test("resolves tool via alias", () => {
|
|
276
|
+
// Set up an alias for the project tool
|
|
277
|
+
addToolToRegistry("test/project-tool", "1.0.0", "project", PROJECT_DIR);
|
|
278
|
+
addAlias("pt", "test/project-tool", "project", PROJECT_DIR);
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
// Resolve using the alias (no slashes = potential alias)
|
|
282
|
+
const result = resolveTool("pt", { startDir: PROJECT_DIR });
|
|
283
|
+
expect(result.manifest.name).toBe("test/project-tool");
|
|
284
|
+
expect(result.location).toBe("project");
|
|
285
|
+
} finally {
|
|
286
|
+
// Clean up
|
|
287
|
+
removeAlias("pt", "project", PROJECT_DIR);
|
|
288
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"), { force: true });
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("alias resolution is case-insensitive (normalized to lowercase)", () => {
|
|
293
|
+
addToolToRegistry("test/project-tool", "1.0.0", "project", PROJECT_DIR);
|
|
294
|
+
addAlias("mytool", "test/project-tool", "project", PROJECT_DIR);
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
// Lowercase alias should work
|
|
298
|
+
const result = resolveTool("mytool", { startDir: PROJECT_DIR });
|
|
299
|
+
expect(result.manifest.name).toBe("test/project-tool");
|
|
300
|
+
|
|
301
|
+
// Uppercase alias should also work (normalized to lowercase)
|
|
302
|
+
const upperResult = resolveTool("MYTOOL", { startDir: PROJECT_DIR });
|
|
303
|
+
expect(upperResult.manifest.name).toBe("test/project-tool");
|
|
304
|
+
|
|
305
|
+
// Mixed case should also work
|
|
306
|
+
const mixedResult = resolveTool("MyTool", { startDir: PROJECT_DIR });
|
|
307
|
+
expect(mixedResult.manifest.name).toBe("test/project-tool");
|
|
308
|
+
} finally {
|
|
309
|
+
removeAlias("mytool", "project", PROJECT_DIR);
|
|
310
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"), { force: true });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("full tool names bypass alias resolution", () => {
|
|
315
|
+
addToolToRegistry("test/project-tool", "1.0.0", "project", PROJECT_DIR);
|
|
316
|
+
// Create an alias that would conflict if checked
|
|
317
|
+
addAlias("test/project-tool", "some/other/tool", "project", PROJECT_DIR);
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
// Full name with slashes should resolve directly, not via alias
|
|
321
|
+
const result = resolveTool("test/project-tool", { startDir: PROJECT_DIR });
|
|
322
|
+
expect(result.manifest.name).toBe("test/project-tool");
|
|
323
|
+
} finally {
|
|
324
|
+
removeAlias("test/project-tool", "project", PROJECT_DIR);
|
|
325
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"), { force: true });
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test("tryResolveTool works with aliases", () => {
|
|
330
|
+
addToolToRegistry("test/project-tool", "1.0.0", "project", PROJECT_DIR);
|
|
331
|
+
addAlias("try-alias", "test/project-tool", "project", PROJECT_DIR);
|
|
332
|
+
|
|
333
|
+
try {
|
|
334
|
+
const result = tryResolveTool("try-alias", { startDir: PROJECT_DIR });
|
|
335
|
+
expect(result).not.toBeNull();
|
|
336
|
+
expect(result?.manifest.name).toBe("test/project-tool");
|
|
337
|
+
} finally {
|
|
338
|
+
removeAlias("try-alias", "project", PROJECT_DIR);
|
|
339
|
+
rmSync(join(PROJECT_ENACT_DIR, "tools.json"), { force: true });
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("non-existent alias returns null from tryResolveTool", () => {
|
|
344
|
+
const result = tryResolveTool("nonexistent-alias", {
|
|
345
|
+
startDir: PROJECT_DIR,
|
|
346
|
+
skipUser: true,
|
|
347
|
+
skipCache: true,
|
|
348
|
+
});
|
|
349
|
+
expect(result).toBeNull();
|
|
350
|
+
});
|
|
351
|
+
});
|
|
272
352
|
});
|