@way_marks/cli 0.5.0 → 0.5.2
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/commands/start.test.js +138 -0
- package/package.json +13 -3
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const net = __importStar(require("net"));
|
|
37
|
+
// ─── kebabCase ────────────────────────────────────────────────────────────────
|
|
38
|
+
// We inline kebabCase here because it is not exported from start.ts.
|
|
39
|
+
// This matches the implementation exactly.
|
|
40
|
+
function kebabCase(str) {
|
|
41
|
+
return str
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[\s_]+/g, '-')
|
|
44
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
45
|
+
.replace(/-+/g, '-')
|
|
46
|
+
.replace(/^-|-$/g, '');
|
|
47
|
+
}
|
|
48
|
+
describe('kebabCase', () => {
|
|
49
|
+
it('lowercases the string', () => {
|
|
50
|
+
expect(kebabCase('MyProject')).toBe('myproject');
|
|
51
|
+
});
|
|
52
|
+
it('replaces spaces with dashes', () => {
|
|
53
|
+
expect(kebabCase('my project')).toBe('my-project');
|
|
54
|
+
});
|
|
55
|
+
it('replaces underscores with dashes', () => {
|
|
56
|
+
expect(kebabCase('my_project')).toBe('my-project');
|
|
57
|
+
});
|
|
58
|
+
it('replaces multiple spaces/underscores with a single dash', () => {
|
|
59
|
+
expect(kebabCase('my __project')).toBe('my-project');
|
|
60
|
+
});
|
|
61
|
+
it('strips special characters', () => {
|
|
62
|
+
expect(kebabCase('my@project!')).toBe('myproject');
|
|
63
|
+
});
|
|
64
|
+
it('collapses multiple dashes', () => {
|
|
65
|
+
expect(kebabCase('my---project')).toBe('my-project');
|
|
66
|
+
});
|
|
67
|
+
it('strips leading dashes', () => {
|
|
68
|
+
expect(kebabCase('-my-project')).toBe('my-project');
|
|
69
|
+
});
|
|
70
|
+
it('strips trailing dashes', () => {
|
|
71
|
+
expect(kebabCase('my-project-')).toBe('my-project');
|
|
72
|
+
});
|
|
73
|
+
it('handles already clean kebab input', () => {
|
|
74
|
+
expect(kebabCase('my-project')).toBe('my-project');
|
|
75
|
+
});
|
|
76
|
+
it('handles numbers in string', () => {
|
|
77
|
+
expect(kebabCase('project-v2-api')).toBe('project-v2-api');
|
|
78
|
+
});
|
|
79
|
+
it('handles all-special-char input returning empty string', () => {
|
|
80
|
+
expect(kebabCase('!!!@@@')).toBe('');
|
|
81
|
+
});
|
|
82
|
+
it('handles typical project directory names', () => {
|
|
83
|
+
expect(kebabCase('ecommerce_backend')).toBe('ecommerce-backend');
|
|
84
|
+
expect(kebabCase('Japan Travel App')).toBe('japan-travel-app');
|
|
85
|
+
expect(kebabCase('my.project.v2')).toBe('myprojectv2');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
// ─── findAvailablePort ────────────────────────────────────────────────────────
|
|
89
|
+
function findAvailablePort(preferred) {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
const server = net.createServer();
|
|
92
|
+
server.listen(preferred, () => {
|
|
93
|
+
const port = server.address().port;
|
|
94
|
+
server.close(() => resolve(port));
|
|
95
|
+
});
|
|
96
|
+
server.on('error', () => {
|
|
97
|
+
// No ceiling in tests — recurse to next port unconditionally
|
|
98
|
+
resolve(findAvailablePort(preferred + 1));
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** Find a free high port dynamically so tests don't depend on 3001 being free */
|
|
103
|
+
function getFreePort() {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const srv = net.createServer();
|
|
106
|
+
srv.listen(0, () => {
|
|
107
|
+
const port = srv.address().port;
|
|
108
|
+
srv.close(() => resolve(port));
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
describe('findAvailablePort', () => {
|
|
113
|
+
it('returns the preferred port when it is free', async () => {
|
|
114
|
+
const free = await getFreePort();
|
|
115
|
+
const port = await findAvailablePort(free);
|
|
116
|
+
expect(port).toBe(free);
|
|
117
|
+
});
|
|
118
|
+
it('falls back to next port when preferred is in use', async () => {
|
|
119
|
+
const base = await getFreePort();
|
|
120
|
+
// Occupy base
|
|
121
|
+
const blocker = net.createServer();
|
|
122
|
+
await new Promise(res => blocker.listen(base, res));
|
|
123
|
+
try {
|
|
124
|
+
const port = await findAvailablePort(base);
|
|
125
|
+
expect(port).not.toBe(base);
|
|
126
|
+
expect(port).toBeGreaterThan(0);
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
await new Promise(res => blocker.close(() => res()));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
it('returns a number (valid port)', async () => {
|
|
133
|
+
const free = await getFreePort();
|
|
134
|
+
const port = await findAvailablePort(free);
|
|
135
|
+
expect(typeof port).toBe('number');
|
|
136
|
+
expect(port).toBeGreaterThan(0);
|
|
137
|
+
});
|
|
138
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@way_marks/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Control what AI agents can do in your codebase",
|
|
5
5
|
"author": "Waymark <hello@waymarks.dev>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,13 +28,23 @@
|
|
|
28
28
|
"dist/**"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
|
-
"build": "tsc"
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"test": "jest",
|
|
33
|
+
"test:coverage": "jest --coverage"
|
|
34
|
+
},
|
|
35
|
+
"jest": {
|
|
36
|
+
"preset": "ts-jest",
|
|
37
|
+
"testEnvironment": "node",
|
|
38
|
+
"testMatch": ["**/src/**/*.test.ts"]
|
|
32
39
|
},
|
|
33
40
|
"dependencies": {
|
|
34
|
-
"@way_marks/server": "0.5.
|
|
41
|
+
"@way_marks/server": "0.5.2"
|
|
35
42
|
},
|
|
36
43
|
"devDependencies": {
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
37
45
|
"@types/node": "^20.11.5",
|
|
46
|
+
"jest": "^30.3.0",
|
|
47
|
+
"ts-jest": "^29.4.9",
|
|
38
48
|
"typescript": "^5.3.3"
|
|
39
49
|
}
|
|
40
50
|
}
|